在 Go 语言中,ctx.Done()
是 context.Context
接口的一个方法,它返回一个只读的通道(<-chan struct{}
),用于监听上下文的取消信号。以下是详细说明和典型用法:
1. 核心作用
- 监听取消事件:当上下文被取消(超时、手动调用
cancel()
或父上下文取消)时,Done()
通道会被关闭,此时可以通过ctx.Err()
获取取消原因。 - 非阻塞检查:通常配合
select
语句实现非阻塞的协程退出逻辑。
2. 基础用法示例
func worker(ctx context.Context) {
for {
select {
case <-ctx.Done():
// 上下文被取消,处理退出逻辑
fmt.Println("Worker stopped:", ctx.Err()) // 输出取消原因(如 "context canceled")
return
default:
// 正常执行任务
fmt.Println("Working...")
time.Sleep(500 * time.Millisecond)
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
go worker(ctx)
time.Sleep(2 * time.Second)
cancel() // 手动触发取消
time.Sleep(1 * time.Second) // 等待 worker 退出
}
输出:
Working...
Working...
Working...
Worker stopped: context canceled
3. 常见场景
(1) HTTP请求超时控制
func fetchAPI(ctx context.Context, url string) (string, error) {
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
data, _ := io.ReadAll(resp.Body)
return string(data), nil
}
// 使用方:
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
result, err := fetchAPI(ctx, "https://example.com")
if errors.Is(err, context.DeadlineExceeded) {
log.Fatal("请求超时")
}
(2) 并发任务快速终止
func runParallelTasks(ctx context.Context) {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
select {
case <-ctx.Done():
fmt.Printf("Task %d aborted\n", id)
return
default:
fmt.Printf("Task %d running\n", id)
time.Sleep(time.Duration(id+1) * time.Second)
}
}(i)
}
cancel() // 模拟中途取消所有任务
}
// Output:
// Task X aborted (所有未完成的任务立即退出)
4. Why Not Directly Check ctx.Err()
?
虽然可以直接调用 ctx.Err()
,但通过 Done()
+ select
能更高效地响应取消事件:
- 避免忙等待:
select
会阻塞直到通道关闭或其它case就绪。 - 原子性保证:通道关闭是线程安全的广播机制。
5. Best Practices
- 始终处理 Done():尤其在循环或长耗时操作中。
- 资源清理:在收到
Done()
后释放连接、文件句柄等资源。 - 避免空值判断:直接监听通道而非检查
ctx == nil
。
如果需要进一步探讨具体场景的实现细节,可以继续提问!
内容由零声教学AI助手提供,问题来源于学员提问