Go并发编程:巧用Context处理超时与取消
在Go语言中,goroutine的并发模型虽然强大,但也带来了超时控制和取消操作的挑战。标准库的context包正是为此而生,它能优雅地传递取消信号、超时时间以及请求范围的值。本文将介绍如何利用context.WithTimeout和select语句实现超时控制。
基础用法
假设我们有一个耗时的任务(如HTTP请求、数据库查询),需要设置超时限制:
func doWork(ctx context.Context) error {
// 模拟耗时任务
select {
case <-time.After(3 * time.Second):
return nil
case <-ctx.Done():
return ctx.Err()
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel() // 确保资源释放
err := doWork(ctx)
if err != nil {
fmt.Println("任务失败:", err) // 输出: 任务失败: context deadline exceeded
}
}
这里doWork通过select同时监听任务完成和context取消。当超时触发,ctx.Done()返回的channel被关闭,任务立即退出并返回超时错误。
更复杂的场景:多个goroutine协作
当启动多个worker时,使用context可以一次性取消所有goroutine:
func worker(ctx context.Context, id int) {
for {
select {
case <-ctx.Done():
fmt.Printf("worker %d 停止\n", id)
return
default:
// 模拟工作
time.Sleep(500 * time.Millisecond)
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
for i := 0; i < 3; i++ {
go worker(ctx, i)
}
time.Sleep(2 * time.Second)
cancel() // 发出取消信号,所有worker停止
time.Sleep(1 * time.Second) // 等待输出
}
通过context.WithCancel,主函数可以在任意时刻通知所有worker停止,避免了goroutine泄漏。
总结
context包是Go并发编程的基石,它提供了:
WithCancel:手动取消WithTimeout/WithDeadline:自动超时WithValue:传递请求范围的值(谨慎使用)
合理使用context能让你的并发程序更健壮、更易于维护。掌握它,写出更优雅的Go代码吧!