8. go并发:Context-多线程并发控制神器
协程如何退出 一般来说,我们执行协程,需要等到协程执行完毕,才能够退出。但是,当我们想要让协程提前退出,就需要一种机制,去控制协程的退出。 以下例子使用select + channel的方式,控制协程的退出 func main() { ch := make(chan bool) var wg sync.WaitGroup go func() { defer wg.Done() // 开启looper协程 looper(ch) }() wg.Add(1) // 5秒后发送中断信号 time.Sleep(time.Second * 5) fmt.Println("Signal to goroutine exit...") ch <- true fmt.Println("Signal sent.") wg.Wait() // 等待协程完全退出 fmt.Println("Exit.") } func looper(ch <-chan bool){ for { select { case <-ch: fmt.Println("go break signal") return default: fmt.Println("looper is running...") time.Sleep(time.Second * 1) } } } 上述例子通过一个ch channel 发送信号,让协程收到信号后终止循环。 使用Context控制协程退出 上述,我们可以通过select+ channel 的方式进行控制协程的退出,但是那是单个协程的退出,当我们想要控制多个协程的退出时,也可以用定义多个channel的方法,但是这会让代码变得十分复杂。 而Context,可以让我们跟踪到每个协程,从而更好地进行限制和控制。 使用 Context 改造上述的代码: func main() { var wg sync.WaitGroup ctx, stop := context.WithCancel(context.Background()) go func() { defer wg.Done() looper(ctx) }() wg.Add(1) time.Sleep(time.Second * 5) fmt.Println("Signal to goroutine exit...") stop() // 向context发送停止指令 fmt.Println("Signal sent.") wg.Wait() fmt.Println("Exit.") } func looper(ctx context.Context){ for { select { case <-ctx.Done(): fmt.Println("go break signal") return default: fmt.Println("looper is running...") time.Sleep(time.Second * 1) } } } 上述代码实现的功能和使用select + channel也是一样的,主要改动为: ...