• Go基础学习笔记(二):错误处理和资源管理、Goroutine、Channel、迷宫的广度优先搜索、http及其他标准库


    7、错误处理和资源管理

    1)、defer调用

    1)defer调用

    • 确保调用在函数结束时发生
    • 参数在defer语句时计算
    • defer列表为后进先出
    func tryDefer() {
    	defer fmt.Println(1)
    	defer fmt.Println(2)
    	panic("error occurred")
    }
    
    func main() {
    	tryDefer() // 2 1
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    func writeFile(filename string) {
    	file, err := os.Create(filename)
    	if err != nil {
    		panic(err)
    	}
    	defer file.Close()
    	
    	writer := bufio.NewWriter(file)
    	defer writer.Flush()
    	fmt.Fprintf(writer, "hello world")
    }
    
    func main() {
    	writeFile("hello world.txt")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    func tryDefer() {
    	for i := 0; i < 100; i++ {
    		defer fmt.Println(i) // 30 29 28 ...
    		if i == 30 {
    			panic("printed too many")
    		}
    	}
    }
    
    func main() {
    	tryDefer()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2)何时使用defer调用

    • Open/Close
    • Lock/Unlock
    • PrintHeader/PrintFooter

    2)、错误处理概念

    func writeFile(filename string) {
    	file, err := os.OpenFile(filename, os.O_EXCL|os.O_CREATE, 0666) // 文件已存在时报错
    	if err != nil {
    		if pathError, ok := err.(*os.PathError); !ok {
    			panic(err)
    		} else {
    			fmt.Println(pathError.Op, pathError.Path, pathError.Err) // open hello world.txt file exists
    		}
    		return
    	}
    	defer file.Close()
    
    	writer := bufio.NewWriter(file)
    	defer writer.Flush()
    	fmt.Fprintf(writer, "hello world")
    }
    
    func main() {
    	writeFile("hello world.txt")
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    3)、服务器统一出错处理

    package filelisting
    
    import (
    	"io/ioutil"
    	"net/http"
    	"os"
    )
    
    func HandleFileListing(writer http.ResponseWriter, request *http.Request) error {
    	path := request.URL.Path[len("/list/"):]
    	file, err := os.Open(path)
    	defer file.Close()
    	if err != nil {
    		return err
    	}
    	all, err := ioutil.ReadAll(file)
    	if err != nil {
    		return err
    	}
    	writer.Write(all)
    	return nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    package main
    
    import (
    	"learngo/errhandling/filelistingserver/filelisting"
    	"log"
    	"net/http"
    	"os"
    )
    
    type appHandler func(writer http.ResponseWriter, request *http.Request) error
    
    func errWrapper(handler appHandler) func(writer http.ResponseWriter, request *http.Request) {
    	return func(writer http.ResponseWriter, request *http.Request) {
    		err := handler(writer, request)
    		if err != nil {
    			log.Printf("Error handling request:%s\n", err.Error())
    			code := http.StatusOK
    			switch {
    			case os.IsNotExist(err):
    				code = http.StatusNotFound
    			case os.IsPermission(err):
    				code = http.StatusForbidden
    			default:
    				code = http.StatusInternalServerError
    			}
    			http.Error(writer, http.StatusText(code), code)
    		}
    	}
    }
    
    func main() {
    	http.HandleFunc("/list/", errWrapper(filelisting.HandleFileListing))
    	err := http.ListenAndServe(":8888", nil)
    	if err != nil {
    		panic(err)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    项目根目录下有hello world.txt,访问http://localhost:8888/list/hello%20world.txt,返回hello world

    访问http://localhost:8888/list/,返回Not Found

    拷贝一个hello world2.txt文件,修改访问权限

    sudo cp hello\ world.txt hello\ world2.txt 
    sudo chmod 500 hello\ world2.txt
    
    • 1
    • 2

    访问http://localhost:8888/list/hello%20world2.txt,返回Forbidden

    4)、panic和recover

    1)panic

    • 停止当前函数执行
    • 一直向上返回,执行每一层的defer
    • 如果没有遇见recover,程序退出

    2)recover

    • 仅在defer调用中使用
    • 获取panic的值
    • 如果无法处理,可重新panic
    func tryRecover() {
    	defer func() {
    		r := recover()
    		if err, ok := r.(error); ok {
    			fmt.Println("Error occurred:", err)
    		} else {
    			panic(r)
    		}
    	}()
    	panic(errors.New("this is an error"))
    }
    
    func main() {
    	tryRecover()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    5)、服务器统一出错处理2

    package filelisting
    
    import (
    	"io/ioutil"
    	"net/http"
    	"os"
    	"strings"
    )
    
    const prefix = "/list/"
    
    type userError string
    
    func (e userError) Error() string {
    	return e.Message()
    }
    
    func (e userError) Message() string {
    	return string(e)
    }
    
    func HandleFileListing(writer http.ResponseWriter, request *http.Request) error {
    	if strings.Index(request.URL.Path, prefix) != 0 {
    		return userError("Path must start with " + prefix)
    	}
    	path := request.URL.Path[len(prefix):]
    	file, err := os.Open(path)
    	defer file.Close()
    	if err != nil {
    		return err
    	}
    	all, err := ioutil.ReadAll(file)
    	if err != nil {
    		return err
    	}
    	writer.Write(all)
    	return nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    package main
    
    import (
    	"learngo/errhandling/filelistingserver/filelisting"
    	"log"
    	"net/http"
    	"os"
    )
    
    type appHandler func(writer http.ResponseWriter, request *http.Request) error
    
    func errWrapper(handler appHandler) func(writer http.ResponseWriter, request *http.Request) {
    	return func(writer http.ResponseWriter, request *http.Request) {
    		defer func() {
    			if r := recover(); r != nil {
    				log.Printf("Panic: %v", r)
    				http.Error(writer, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
    			}
    		}()
    
    		err := handler(writer, request)
    		if err != nil {
    			log.Printf("Error handling request:%s\n", err.Error())
    			if userErr, ok := err.(userError); ok {
    				http.Error(writer, userErr.Message(), http.StatusBadRequest)
    				return
    			}
    			code := http.StatusOK
    			switch {
    			case os.IsNotExist(err):
    				code = http.StatusNotFound
    			case os.IsPermission(err):
    				code = http.StatusForbidden
    			default:
    				code = http.StatusInternalServerError
    			}
    			http.Error(writer, http.StatusText(code), code)
    		}
    	}
    }
    
    type userError interface {
    	error
    	Message() string
    }
    
    func main() {
    	http.HandleFunc("/", errWrapper(filelisting.HandleFileListing))
    	err := http.ListenAndServe(":8888", nil)
    	if err != nil {
    		panic(err)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53

    访问http://localhost:8888/,返回Path must start with /list/

    error vs panic:

    • 意料之中的:使用error。如:文件打不开
    • 意料之外的:使用panic。如:数组越界

    8、Goroutine

    1)、goroutine

    • 轻量级线程
    • 非抢占式多任务处理,由协程主动交出控制权
    • 编译器/解释器/虚拟机层面的多任务
    • 多个协程可能在一个或多个线程上运行
    func main() {
    	for i := 0; i < 10; i++ {
    		go func(i int) {
    			for {
    				fmt.Printf("Hello from goroutine %d\n", i)
    			}
    		}(i)
    	}
    	time.Sleep(time.Millisecond)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    func main() {
    	var a [10]int
    	for i := 0; i < 10; i++ {
    		go func() {
    			for {
    				a[i]++
    				runtime.Gosched()
    			}
    		}()
    	}
    	time.Sleep(time.Millisecond)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    执行报错:

    panic: runtime error: index out of range [10] with length 10
    
    • 1

    检测数据访问冲突:

    go run -race goroutine.go
    
    • 1

    2)、Go语言的调度器

    1)协程coroutine

    子程序是协程的一个特例

    2)goroutine

    3)goroutine的定义

    • 任何函数只需加上go就能送给调度器运行
    • 不需要在定义时区分是否是异步函数
    • 调度器在合适点进行切换
    • 使用-race来检测数据访问冲突

    4)goroutine可能的切换点

    • I/O,select
    • channel
    • 等待锁
    • 函数调用(有时)
    • runtime.Gosched()

    只是参考,不能保证切换,不能保证在其他地方不切换

    10、Channel

    1)、channel

    func channelDemo() {
    	c := make(chan int)
    	go func() {
    		for {
    			n := <-c
    			fmt.Println(n)
    		}
    	}()
    	c <- 1
    	c <- 2
    	time.Sleep(time.Second)
    }
    
    func main() {
    	channelDemo()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    channel数组:

    func worker(id int, c chan int) {
    	for {
    		fmt.Printf("Worker %d received %c\n", id, <-c)
    	}
    }
    
    func channelDemo() {
    	var channels [10]chan int
    	for i := 0; i < 10; i++ {
    		channels[i] = make(chan int)
    		go worker(i, channels[i])
    	}
    	for i := 0; i < 10; i++ {
    		channels[i] <- 'a' + i
    	}
    	for i := 0; i < 10; i++ {
    		channels[i] <- 'A' + i
    	}
    	time.Sleep(time.Second)
    }
    
    func main() {
    	channelDemo()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    channel作为返回值:

    func createWorker(id int) chan<- int {
    	c := make(chan int)
    	go func() {
    		for {
    			fmt.Printf("Worker %d received %c\n", id, <-c)
    		}
    	}()
    	return c
    }
    
    func channelDemo() {
    	var channels [10]chan<- int
    	for i := 0; i < 10; i++ {
    		channels[i] = createWorker(i)
    	}
    	for i := 0; i < 10; i++ {
    		channels[i] <- 'a' + i
    	}
    	for i := 0; i < 10; i++ {
    		channels[i] <- 'A' + i
    	}
    	time.Sleep(time.Second)
    }
    
    func main() {
    	channelDemo()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    channel缓冲区:

    func worker(id int, c chan int) {
    	for {
    		fmt.Printf("Worker %d received %c\n", id, <-c)
    	}
    }
    
    func bufferedChannel() {
    	c := make(chan int, 3)
    	go worker(0, c)
    	c <- 'a'
    	c <- 'b'
    	c <- 'c'
    	time.Sleep(time.Second)
    }
    
    func main() {
    	bufferedChannel()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    关闭channel:

    func worker(id int, c chan int) {
    	for n := range c {
    		fmt.Printf("Worker %d received %c\n", id, n)
    	}
    }
    
    func channelClose() {
    	c := make(chan int, 3)
    	go worker(0, c)
    	c <- 'a'
    	c <- 'b'
    	c <- 'c'
    	close(c)
    	time.Sleep(time.Second)
    }
    
    func main() {
    	channelClose()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • channel
    • buffered channel
    • range

    2)、使用Channel等待任务结束

    type worker struct {
    	in   chan int
    	done chan bool
    }
    
    func doWorker(id int, c chan int, done chan bool) {
    	for n := range c {
    		fmt.Printf("Worker %d received %c\n", id, n)
    		done <- true
    	}
    }
    
    func createWorker(id int) worker {
    	w := worker{
    		in:   make(chan int),
    		done: make(chan bool),
    	}
    	go doWorker(id, w.in, w.done)
    	return w
    }
    
    func channelDemo() {
    	var workers [10]worker
    	for i := 0; i < 10; i++ {
    		workers[i] = createWorker(i)
    	}
    	for i, worker := range workers {
    		worker.in <- 'a' + i
    	}
    	for _, worker := range workers {
    		<-worker.done
    	}
    	for i, worker := range workers {
    		worker.in <- 'A' + i
    	}
    	for _, worker := range workers {
    		<-worker.done
    	}
    }
    
    func main() {
    	channelDemo()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    使用WaitGroup实现:

    type worker struct {
    	in   chan int
    	done func()
    }
    
    func doWorker(id int, w worker) {
    	for n := range w.in {
    		fmt.Printf("Worker %d received %c\n", id, n)
    		w.done()
    	}
    }
    
    func createWorker(id int, wg *sync.WaitGroup) worker {
    	w := worker{
    		in: make(chan int),
    		done: func() {
    			wg.Done()
    		},
    	}
    	go doWorker(id, w)
    	return w
    }
    
    func channelDemo() {
    	var wg sync.WaitGroup
    	wg.Add(20)
    	var workers [10]worker
    	for i := 0; i < 10; i++ {
    		workers[i] = createWorker(i, &wg)
    	}
    	for i, worker := range workers {
    		worker.in <- 'a' + i
    	}
    	for i, worker := range workers {
    		worker.in <- 'A' + i
    	}
    	wg.Wait()
    }
    
    func main() {
    	channelDemo()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    3)、使用Channel进行树的遍历

    package node
    
    import "fmt"
    
    type TreeNode struct {
    	Value       int
    	Left, Right *TreeNode
    }
    
    func (node TreeNode) Print() {
    	fmt.Print(node.Value, " ")
    }
    
    func CreateNode(value int) *TreeNode {
    	return &TreeNode{Value: value}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    package node
    
    import (
    	"fmt"
    )
    
    func (node *TreeNode) Traverse() {
    	node.TraverseFunc(func(node *TreeNode) {
    		node.Print()
    	})
    	fmt.Println()
    }
    
    // 中序遍历
    func (node *TreeNode) TraverseFunc(f func(*TreeNode)) {
    	if node == nil {
    		return
    	}
    	node.Left.TraverseFunc(f)
    	f(node)
    	node.Right.TraverseFunc(f)
    }
    
    func (node *TreeNode) TraverseWithChannel() chan *TreeNode {
    	out := make(chan *TreeNode)
    	go func() {
    		node.TraverseFunc(func(node *TreeNode) {
    			out <- node
    		})
    		close(out)
    	}()
    	return out
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    package main
    
    import (
    	"fmt"
    	"learngo/tree/node"
    )
    
    func main() {
    	root := node.CreateNode(3)
    	root.Left = node.CreateNode(0)
    	root.Right = node.CreateNode(5)
    	root.Right.Left = node.CreateNode(4)
    	root.Left.Right = node.CreateNode(2)
    	root.Traverse() // 0 2 3 4 5
    	nodeCount := 0
    	root.TraverseFunc(func(node *node.TreeNode) {
    		nodeCount++
    	})
    	fmt.Println("node count:", nodeCount) // node count: 5
    
    	c := root.TraverseWithChannel()
    	maxNode := 0
    	for node := range c {
    		if node.Value > maxNode {
    			maxNode = node.Value
    		}
    	}
    	fmt.Println("max node value:", maxNode) // max node value: 5
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    4)、用select进行调度

    func generator() chan int {
    	out := make(chan int)
    	go func() {
    		i := 0
    		for {
    			time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond)
    			out <- i
    			i++
    		}
    	}()
    	return out
    }
    
    func worker(id int, c chan int) {
    	for n := range c {
    		time.Sleep(time.Second)
    		fmt.Printf("Worker %d received %d\n", id, n)
    	}
    }
    
    func createWorker(id int) chan<- int {
    	c := make(chan int)
    	go worker(id, c)
    	return c
    }
    
    func main() {
    	var c1, c2 = generator(), generator()
    	var worker = createWorker(0)
    	var values []int
    	tm := time.After(10 * time.Second)
    	tick := time.Tick(time.Second)
    	for {
    		var activeWorker chan<- int
    		var activeValue int
    		if len(values) > 0 {
    			activeWorker = worker
    			activeValue = values[0]
    		}
    		select {
    		case n := <-c1:
    			values = append(values, n)
    		case n := <-c2:
    			values = append(values, n)
    		case activeWorker <- activeValue:
    			values = values[1:]
    		// 200毫秒没有数据打印timeout
    		case <-time.After(200 * time.Millisecond):
    			fmt.Println("timeout")
    		// 每秒打印队列长度
    		case <-tick:
    			fmt.Println("queue len =", len(values))
    		// 程序运行10秒后结束
    		case <-tm:
    			fmt.Println("end")
    			return
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • select的使用
    • 定时器的使用
    • 在select中使用nil channel

    5)、传统同步机制

    • WaitGroup
    • Mutex
    • Cond

    实现atomicInt:

    package main
    
    import (
    	"fmt"
    	"sync"
    	"time"
    )
    
    type atomicInt struct {
    	value int
    	lock  sync.Mutex
    }
    
    func (a *atomicInt) increment() {
    	a.lock.Lock()
    	defer a.lock.Unlock()
    	a.value++
    }
    
    func (a *atomicInt) get() int {
    	a.lock.Lock()
    	defer a.lock.Unlock()
    	return a.value
    }
    
    func main() {
    	var a atomicInt
    	for i := 0; i < 1000; i++ {
    		go func() {
    			a.increment()
    		}()
    	}
    	time.Sleep(5 * time.Second)
    	fmt.Println(a.get())
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    6)、并发模式(上)

    • 生成器
    • 服务/任务
    • 同时等待多个服务
    func msgGen(name string) <-chan string {
    	c := make(chan string)
    	go func() {
    		i := 0
    		for {
    			time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
    			c <- fmt.Sprintf("service %s: message %d", name, i)
    			i++
    		}
    	}()
    	return c
    }
    
    func fanIn(c1, c2 <-chan string) chan string {
    	c := make(chan string)
    	go func() {
    		for {
    			c <- <-c1
    		}
    	}()
    	go func() {
    		for {
    			c <- <-c2
    		}
    	}()
    	return c
    }
    
    func main() {
    	m1, m2 := msgGen("service1"), msgGen("service2")
    	m := fanIn(m1, m2)
    	for {
    		fmt.Println(<-m)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    7)、并发模式(下)

    func msgGen(name string) <-chan string {
    	c := make(chan string)
    	go func() {
    		i := 0
    		for {
    			time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
    			c <- fmt.Sprintf("service %s: message %d", name, i)
    			i++
    		}
    	}()
    	return c
    }
    
    func fanInBySelect(c1, c2 <-chan string) chan string {
    	c := make(chan string)
    	go func() {
    		for {
    			select {
    			case m := <-c1:
    				c <- m
    			case m := <-c2:
    				c <- m
    			}
    		}
    	}()
    	return c
    }
    
    func main() {
    	m1, m2 := msgGen("service1"), msgGen("service2")
    	m := fanInBySelect(m1, m2)
    	for {
    		fmt.Println(<-m)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    func msgGen(name string) <-chan string {
    	c := make(chan string)
    	go func() {
    		i := 0
    		for {
    			time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
    			c <- fmt.Sprintf("service %s: message %d", name, i)
    			i++
    		}
    	}()
    	return c
    }
    
    func fanIn(chs ...<-chan string) chan string {
    	c := make(chan string)
    	for _, ch := range chs {
    		go func(in <-chan string) {
    			for {
    				c <- <-in
    			}
    		}(ch)
    	}
    	return c
    }
    
    func main() {
    	m1, m2 := msgGen("service1"), msgGen("service2")
    	m := fanIn(m1, m2)
    	for {
    		fmt.Println(<-m)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    8)、并发任务的控制

    非阻塞等待:

    func msgGen(name string) <-chan string {
    	c := make(chan string)
    	go func() {
    		i := 0
    		for {
    			time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)
    			c <- fmt.Sprintf("service %s: message %d", name, i)
    			i++
    		}
    	}()
    	return c
    }
    
    func nonBlockingWait(c <-chan string) (string, bool) {
    	select {
    	case m := <-c:
    		return m, true
    	default:
    		return "", false
    	}
    }
    
    func main() {
    	m1, m2 := msgGen("service1"), msgGen("service2")
    	for {
    		fmt.Println(<-m1)
    		if m, ok := nonBlockingWait(m2); ok {
    			fmt.Println(m)
    		} else {
    			fmt.Println("no message from service2")
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    超时机制:

    func msgGen(name string) <-chan string {
    	c := make(chan string)
    	go func() {
    		i := 0
    		for {
    			time.Sleep(time.Duration(rand.Intn(5000)) * time.Millisecond)
    			c <- fmt.Sprintf("service %s: message %d", name, i)
    			i++
    		}
    	}()
    	return c
    }
    
    func timeoutWait(c <-chan string, timeout time.Duration) (string, bool) {
    	select {
    	case m := <-c:
    		return m, true
    	case <-time.After(timeout):
    		return "", false
    	}
    }
    
    func main() {
    	m1 := msgGen("service1")
    	for {
    		if m, ok := timeoutWait(m1, 2*time.Second); ok {
    			fmt.Println(m)
    		} else {
    			fmt.Println("timeout")
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    任务中断/退出:

    func msgGen(name string, done chan struct{}) <-chan string {
    	c := make(chan string)
    	go func() {
    		i := 0
    		for {
    			select {
    			case <-time.After(time.Duration(rand.Intn(5000)) * time.Millisecond):
    				c <- fmt.Sprintf("service %s: message %d", name, i)
    			case <-done:
    				fmt.Println("cleaning up")
    				return
    			}
    			i++
    		}
    	}()
    	return c
    }
    
    func timeoutWait(c <-chan string, timeout time.Duration) (string, bool) {
    	select {
    	case m := <-c:
    		return m, true
    	case <-time.After(timeout):
    		return "", false
    	}
    }
    
    func main() {
    	done := make(chan struct{})
    	m1 := msgGen("service1", done)
    	for i := 0; i < 5; i++ {
    		if m, ok := timeoutWait(m1, time.Second); ok {
    			fmt.Println(m)
    		} else {
    			fmt.Println("timeout")
    		}
    	}
    	done <- struct{}{}
    	time.Sleep(time.Second)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    优雅退出:

    func msgGen(name string, done chan struct{}) <-chan string {
    	c := make(chan string)
    	go func() {
    		i := 0
    		for {
    			select {
    			case <-time.After(time.Duration(rand.Intn(5000)) * time.Millisecond):
    				c <- fmt.Sprintf("service %s: message %d", name, i)
    			case <-done:
    				fmt.Println("cleaning up")
    				time.Sleep(2 * time.Second)
    				fmt.Println("cleanup done")
    				done <- struct{}{}
    				return
    			}
    			i++
    		}
    	}()
    	return c
    }
    
    func timeoutWait(c <-chan string, timeout time.Duration) (string, bool) {
    	select {
    	case m := <-c:
    		return m, true
    	case <-time.After(timeout):
    		return "", false
    	}
    }
    
    func main() {
    	done := make(chan struct{})
    	m1 := msgGen("service1", done)
    	for i := 0; i < 5; i++ {
    		if m, ok := timeoutWait(m1, time.Second); ok {
    			fmt.Println(m)
    		} else {
    			fmt.Println("timeout")
    		}
    	}
    	done <- struct{}{}
    	<-done
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    10、迷宫的广度优先搜索

    迷宫文件maze.in

    6 5
    0 1 0 0 0
    0 0 0 1 0
    0 1 0 1 0
    1 1 1 0 0
    0 1 0 0 1
    0 1 0 0 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    代码实现:

    func readMaze(filename string) [][]int {
    	file, err := os.Open(filename)
    	if err != nil {
    		panic(err)
    	}
    	var row, col int
    	fmt.Fscanf(file, "%d %d", &row, &col)
    	maze := make([][]int, row)
    	for i := range maze {
    		maze[i] = make([]int, col)
    		for j := range maze[i] {
    			fmt.Fscanf(file, "%d", &maze[i][j])
    		}
    	}
    	return maze
    }
    
    type point struct {
    	i, j int
    }
    
    var dirs = [4]point{{-1, 0}, {0, -1}, {1, 0}, {0, 1}}
    
    func (p point) add(r point) point {
    	return point{p.i + r.i, p.j + r.j}
    }
    
    func (p point) at(grid [][]int) (int, bool) {
    	if p.i < 0 || p.i >= len(grid) {
    		return 0, false
    	}
    	if p.j < 0 || p.j >= len(grid[p.i]) {
    		return 0, false
    	}
    	return grid[p.i][p.j], true
    }
    
    func walk(maze [][]int, start, end point) [][]int {
    	steps := make([][]int, len(maze))
    	for i := range steps {
    		steps[i] = make([]int, len(maze[i]))
    	}
    	Q := []point{start}
    	for len(Q) > 0 {
    		cur := Q[0]
    		Q = Q[1:]
    		if cur == end {
    			break
    		}
    		for _, dir := range dirs {
    			next := cur.add(dir)
    			// maze at next is 0
    			// and steps at next is 0
    			// and next != start
    			val, ok := next.at(maze)
    			if !ok || val == 1 {
    				continue
    			}
    			val, ok = next.at(steps)
    			if !ok || val != 0 {
    				continue
    			}
    			if next == start {
    				continue
    			}
    			curSteps, _ := cur.at(steps)
    			steps[next.i][next.j] = curSteps + 1
    			Q = append(Q, next)
    		}
    	}
    	return steps
    }
    
    func main() {
    	maze := readMaze("maze/maze.in")
    	steps := walk(maze, point{0, 0}, point{len(maze) - 1, len(maze[0]) - 1})
    	for _, row := range steps {
    		for _, val := range row {
    			fmt.Printf("%3d", val)
    		}
    		fmt.Println()
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    11、http及其他标准库

    1)、http标准库

    1)使用http客户端发送请求

    func main() {
    	resp, err := http.Get("https://www.imooc.com")
    	if err != nil {
    		panic(err)
    	}
    	defer resp.Body.Close()
    	s, err := httputil.DumpResponse(resp, true)
    	if err != nil {
    		panic(err)
    	}
    	fmt.Printf("%s\n", s)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2)使用http.Client控制请求头部等

    func main() {
    	request, err := http.NewRequest(http.MethodGet, "https://www.imooc.com", nil)
    	request.Header.Add("User-Agent",
    		"Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1")
    	client := http.Client{
    		CheckRedirect: func(
    			req *http.Request,
    			via []*http.Request) error {
    			fmt.Println("Redirect:", req)
    			return nil
    		},
    	}
    	resp, err := client.Do(request)
    	if err != nil {
    		panic(err)
    	}
    	defer resp.Body.Close()
    	s, err := httputil.DumpResponse(resp, true)
    	if err != nil {
    		panic(err)
    	}
    	fmt.Printf("%s\n", s)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2)、json数据格式的处理

    type Order struct {
    	Id         string  `json:"id"`
    	Name       string  `json:"name,omitempty"` // omitempty:省略空
    	Quantity   int     `json:"quantity"`
    	TotalPrice float64 `json:"total_price"`
    }
    
    func main() {
    	o := Order{
    		Id:         "1234",
    		Name:       "learn go",
    		Quantity:   3,
    		TotalPrice: 30,
    	}
    	s, _ := json.Marshal(o)
    	fmt.Printf("%s\n", s) // {"id":"1234","name":"learn go","quantity":3,"total_price":30}
    
    	o2 := Order{
    		Id:         "1234",
    		Name:       "",
    		Quantity:   3,
    		TotalPrice: 30,
    	}
    	s2, _ := json.Marshal(o2)
    	fmt.Printf("%s\n", s2) // {"id":"1234","quantity":3,"total_price":30}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    type OrderItem struct {
    	Id    string  `json:"id"`
    	Name  string  `json:"name"`
    	Price float64 `json:"price"`
    }
    
    type Order struct {
    	Id         string     `json:"id"`
    	Item       *OrderItem `json:"item"`
    	Quantity   int        `json:"quantity"`
    	TotalPrice float64    `json:"total_price"`
    }
    
    func main() {
    	o := Order{
    		Id: "1234",
    		Item: &OrderItem{
    			Id:    "item_1",
    			Name:  "learn go",
    			Price: 15,
    		},
    		Quantity:   3,
    		TotalPrice: 30,
    	}
    	s, _ := json.Marshal(o)
    	fmt.Printf("%s\n", s) // {"id":"1234","item":{"id":"item_1","name":"learn go","price":15},"quantity":3,"total_price":30}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    type OrderItem struct {
    	Id    string  `json:"id"`
    	Name  string  `json:"name"`
    	Price float64 `json:"price"`
    }
    
    type Order struct {
    	Id         string      `json:"id"`
    	Items      []OrderItem `json:"items"`
    	TotalPrice float64     `json:"total_price"`
    }
    
    func main() {
    	marshal()
    	unmarshal()
    }
    
    func marshal() {
    	o := Order{
    		Id: "1234",
    		Items: []OrderItem{
    			{
    				Id:    "item_1",
    				Name:  "learn go",
    				Price: 15,
    			},
    			{
    				Id:    "item_2",
    				Name:  "interview",
    				Price: 10,
    			},
    		},
    		TotalPrice: 20,
    	}
    	s, _ := json.Marshal(o)
    	fmt.Printf("%s\n", s)
    }
    
    func unmarshal() {
    	s := `{"id":"1234","items":[{"id":"item_1","name":"learn go","price":15},{"id":"item_2","name":"interview","price":10}],"total_price":20}`
    	var o Order
    	json.Unmarshal([]byte(s), &o)
    	fmt.Printf("%+v\n", o)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    3)、第三方API数据格式的解析技巧

    func main() {
    	parseNLP()
    }
    
    func parseNLP() {
    	res := `{
    "data": [
        {
            "synonym":"",
            "weight":"0.6",
            "word": "真丝",
            "tag":"材质"
        },
        {
            "synonym":"",
            "weight":"0.8",
            "word": "韩都衣舍",
            "tag":"品牌"
        },
        {
            "synonym":"连身裙;联衣裙",
            "weight":"1.0",
            "word": "连衣裙",
            "tag":"品类"
        }
    ]
    }`
    	m := struct {
    		Data []struct {
    			Synonym string `json:"synonym"`
    			Tag     string `json:"tag"`
    		} `json:"data"`
    	}{}
    	err := json.Unmarshal([]byte(res), &m)
    	if err != nil {
    		panic(err)
    	}
    	fmt.Printf("%+v, %+v\n", m.Data[2].Synonym, m.Data[2].Tag)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    4)、gin框架介绍

    package main
    
    import (
    	"github.com/gin-gonic/gin"
    	"go.uber.org/zap"
    	"math/rand"
    	"time"
    )
    
    const keyRequestId = "requestId"
    
    func main() {
    	r := gin.Default()
    	logger, err := zap.NewProduction()
    	if err != nil {
    		panic(err)
    	}
    	r.Use(func(c *gin.Context) {
    		s := time.Now()
    		c.Next()
    		// path, response code, log latency
    		logger.Info("incoming request",
    			zap.String("path", c.Request.URL.Path),
    			zap.Int("status", c.Writer.Status()),
    			zap.Duration("elapsed", time.Now().Sub(s)))
    	}, func(c *gin.Context) {
    		c.Set(keyRequestId, rand.Int())
    		c.Next()
    	})
    	r.GET("/ping", func(c *gin.Context) {
    		h := gin.H{
    			"message": "pong",
    		}
    		if rid, exists := c.Get(keyRequestId); exists {
    			h[keyRequestId] = rid
    		}
    		c.JSON(200, h)
    	})
    	r.GET("/hello", func(c *gin.Context) {
    		c.String(200, "hello")
    	})
    	r.Run()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    对应课程

    Google资深工程师深度讲解Go语言

  • 相关阅读:
    从零到一搭建基础架构(8)-业务服务接入基础架构
    自定义mock服务器
    海勒姆法则(Hyrum‘s Law)
    【设计模式】Java设计模式 - 享元模式
    使用 bitnami/postgresql-repmgr 镜像快速设置 PostgreSQL HA
    linux调试技巧
    武汉某厂前端中高级面试题一面
    ssm基于微信小程序的食堂线上预约点餐系统--计算机毕业设计
    MongoDB基本查询语句
    APP自动化测试,Appium+PO模式+Pytest框架实战—项目案例
  • 原文地址:https://blog.csdn.net/qq_40378034/article/details/125570420