Go使用chan或context退出协程示例详解
问题
go两个协程使用了同一个文件句柄,其中一个协程关闭了这个文件句柄并退出了协程,如何及时通知另一个协程退出?
分析
当一个协程关闭了这个文件对象后,底层文件的文件描述符就会被释放。
这个时候,另一个协程还持有着同一个文件对象,但其实对应的文件描述符已经不存在了。
所以当第二个协程通过这个文件对象再对文件进行操作的时候,例如读写文件等,就会发生异常,比如文件描述符不存在错误等。
解决
为了避免这个问题,共享文件对象的多个协程需要通过一个通道或锁进行协调:
每个协程在使用文件对象前需要获得锁或从通道接收通知。
最后关闭文件对象的协程在关闭后,通过通道或解锁来通知其他协程对象已经无效。
其他协程收到通知后,就不再对这个已关闭的文件对象进行操作。
1. 使用 channel 通道
在主协程中,可以定义一个 channel,用来通知其它协程退出。协程在执行时可以监听这个 channel,一旦接收到退出通知,就可以进行清理工作,并退出协程。
quit := make(chan bool) go func() { defer fmt.Println("Goroutine exit") for { select { case <-quit: return default: // ... } } }() // 主协程中发送退出通知 quit <- true
2. 使用 context 包
Go 语言标准库中提供了 context 包,可以用来控制协程的生命周期。
在主协程中可以创建一个 context 对象,并将其传递给协程,然后调用 cancel 方法,通知所有协程退出。
context.Background() 是 Go 上下文系统中一个重要的初始预定义上下文值,代表了一个没有截止时间限制的空上下文环境。
1.使用WithCancel函数创建上下文和取消函数
ctx, cancel := context.WithCancel(context.Background()) go func(ctx context.Context) { for { select { case <-ctx.Done(): fmt.Println("exit") return default: // 执行一些耗时的操作 time.Sleep(1 * time.Second) fmt.Println("running") } } }(ctx) time.Sleep(5 * time.Second) // cancel() fmt.Println("cancelled") time.Sleep(1 * time.Second)
2.使用Deadline超时结束协程
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second))
3.使用WithTimeout限定操作时间
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
以上就是Go使用chan或context退出协程示例详解的详细内容,更多关于Go chan context退出协程的资料请关注脚本之家其它相关文章!
相关文章
更高效的GoLevelDB:shardingdb实现分片和并发读写操作
这篇文章主要介绍了更高效的GoLevelDB:shardingdb实现分片和并发读写操作的相关资料,需要的朋友可以参考下2023-09-09
最新评论