[TOC] ## goroutine ### demo -通过延迟来保证并发的执行 ``` func say(s string) { //runtime.Gosched()表示让CPU把时间片让给别人,下次某个时候继续恢复执行该goroutine。 runtime.Gosched() fmt.Println(s) } func main() { go say("world") //需要设置两秒 延迟 ,不然可能并发来不及执行 time.Sleep(1 * time.Second) } ``` ### 通过channel 来保证并发的执执行 ``` func main() { done := make(chan int, 1) // 带缓存的管道 go demo(done) <-done } func demo(done chan int) { fmt.Println("你好, 世界") done <- 1 } ``` ### 保证 多个并发通过完成 方法一. ``` func main() { done := make(chan int, 10) // 带 10 个缓存 // 开N个后台打印线程 for i := 0; i < cap(done); i++ { go func(){ fmt.Println("你好, 世界") done <- 1 }() } // 等待N个后台线程完成 for i := 0; i < cap(done); i++ { <-done } } ``` 方法二 ``` func main() { var wg sync.WaitGroup // 开N个后台打印线程 for i := 0; i < 10; i++ { wg.Add(1) go func() { fmt.Println("你好, 世界") wg.Done() }() } // 等待N个后台线程完成 wg.Wait() } ``` ### sync.Mutex 保证并发安全 ``` package main import ( "fmt" "sync" "time" ) // SafeCounter 的并发使用是安全的。 type SafeCounter struct { v map[string]int mux sync.Mutex } func (c *SafeCounter) Inc(key string) { c.mux.Lock() // Lock 之后同一时刻只有一个 goroutine 能访问 c.v c.v[key]++ c.mux.Unlock() } func (c *SafeCounter) Value(key string) int { c.mux.Lock() defer c.mux.Unlock() return c.v[key] } func main() { c := SafeCounter{v: make(map[string]int)} //需要在实例化时,初始化make for i := 0; i < 1000; i++ { go c.Inc("somekey") } time.Sleep(time.Second) fmt.Println(c.Value("somekey")) } ``` ## channels > 必须使用make 创建channel > channel接收和发送数据都是阻塞的 ``` ci := make(chan int) cs := make(chan string) cf := make(chan interface{}) ``` 传输与接收 ``` ch <- v // 发送v到channel ch. v := <-ch // 从ch中接收数据,并赋值给v ``` ### demo ``` func sum(a int, c chan int) { c <- a // send total to c } func main() { c := make(chan int) go sum(3, c) go sum(4, c) go sum(5, c) x := <-c //3 y := <-c //4 z := <-c //5 } ``` ### Buffered Channels > ch:= make(chan bool, 4),创建了可以存储4个元素的bool 型channel。在这个channel 中,前4个元素可以**无阻塞**的写入。当写入第5个元素时,代码将会阻塞,直到其他goroutine从channel 中读取一些元素,腾出空间 ``` //报错 c := make(chan int, 1) c <- 1 c <- 2 fmt.Println(<-c) fmt.Println(<-c) //不报错 c = make(chan int, 1) c <- 1 fmt.Println(<-c) //先输出一个值,才能再次加入 c <- 2 fmt.Println(<-c) ``` ### 使用Range 取出 Channels 的值 ``` func fibonacci(c chan int) { n := cap(c) for i := 0; i < n; i++ { //添加延迟,使效果更加明显 time.Sleep(500 * time.Millisecond) c <- i } close(c) } func main() { c := make(chan int, 10) go fibonacci(c) for i := range c { fmt.Println(i) } } ``` ## Select > 通过select可以监听不同 channel上的数据流动。 > select默认是阻塞的 ``` func fibonacci(c, quit chan int) { x := 1 for { select { case c <- x: x++ case <-quit: fmt.Println("quit") return } } } func main() { c := make(chan int) quit := make(chan int) go func() { fmt.Println(<-c) //1 fmt.Println(<-c) //2 quit <- 0 //把0 放入quit channel }() fibonacci(c, quit) } ``` ## Context 上下文 ,控制多个goroutine ``` func main() { ctx, cancel := context.WithCancel(context.Background()) go watch(ctx, "【监控1】") go watch(ctx, "【监控2】") go watch(ctx, "【监控3】") time.Sleep(10 * time.Second) fmt.Println("可以了,通知监控停止") cancel() //为了检测监控过是否停止,如果没有监控输出,就表示停止了 time.Sleep(5 * time.Second) } func watch(ctx context.Context, name string) { for { select { case <-ctx.Done(): fmt.Println(name, "监控退出,停止了...") return default: fmt.Println(name, "goroutine监控中...") time.Sleep(2 * time.Second) } } } ```