package main import ( "context" "fmt" "time" ) func main() { // 使用context.Background()创建一个空上下文 ctx, cancel := context.WithCancel(context.Background()) // 启动协程1 go func(ctx context.Context) { counter := 0 // 设置一个计数器,用于确保任务只执行一次 for { select { case <-ctx.Done(): // 收到上下文的cancel信号 fmt.Println("协程1收到上下文的cancel信号,即将停止任务") return default: if counter == 0 { counter = 1 fmt.Println("协程1开始执行任务……") } } } }(ctx) // 启动协程2 go func(ctx context.Context) { counter := 0 // 设置一个计数器,用于确保任务只执行一次 for { select { case <-ctx.Done(): // 收到上下文的cancel信号 fmt.Println("协程2收到上下文的cancel信号,即将停止任务") return default: if counter == 0 { counter = 1 fmt.Println("协程2开始执行任务……") } } } }(ctx) // 等待3秒后取消上下文 time.Sleep(time.Second * 3) cancel() // 再次等待3秒让两个协程有时间执行 time.Sleep(time.Second * 3) fmt.Println("main()执行完毕") } // ========== 输出过程·开始 ========== // // 协程2开始执行任务…… // 协程1开始执行任务…… // 协程1收到上下文的cancel信号,即将停止任务 // 协程2收到上下文的cancel信号,即将停止任务 // main()执行完毕 // ========== 输出过程·结束 ========== // // 说明:由于任务调度的关系,两个协程哪个先执行是无法确定的,同理哪个先停止也是无法确定的。 // ========== 总结 ========== // // 1、上下文可以用于协程嵌套,即可以控制子协程和孙子协程,最典型的使用场景是当父协程由于某种原因需要停止时, // 还要通知其下子协程和孙子协程也停止执行,这时就可以通过上下文发出cancel信号来通知。
Copyright © 2024 码农人生. All Rights Reserved