ZBLOG

ctx.Done():

在 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助手提供,问题来源于学员提问

本站部分文章来源于网络,版权归原作者所有,如有侵权请联系站长删除。
转载请注明出处:https://golang.0voice.com/?id=24044

分享:
扫描分享到社交APP
上一篇
下一篇
发表列表
游客 游客
此处应有掌声~
评论列表

还没有评论,快来说点什么吧~

联系我们

在线咨询: 点击这里给我发消息

微信号:3007537140

上班时间: 10:30-22:30

关注我们
x

注册

已经有帐号?