正确使用上下文控制业务流
【摘要】 1 简介本文简介context 的正确用法,循序渐进 + 可运行的例子给你讲清楚:如何用 ctx 管理任务是否完成 / 是否取消。 2 理清概念ctx 只做一件事:广播“该停了”这个信号任务是否完成 → 由任务自己决定ctx 只负责:取消 / 超时 / 上游结束 3 场景 1:最基础 —— 用 ctx 控制任务退出任务函数 func worker(ctx context.Contex...
1 简介
本文简介context 的正确用法,循序渐进 + 可运行的例子给你讲清楚:如何用 ctx 管理任务是否完成 / 是否取消。

2 理清概念
ctx 只做一件事:广播“该停了”这个信号
任务是否完成 → 由任务自己决定
ctx 只负责:取消 / 超时 / 上游结束
3 场景 1:最基础 —— 用 ctx 控制任务退出
任务函数
func worker(ctx context.Context) error {
for {
select {
case <-ctx.Done():
// 上游取消 / 超时
return ctx.Err()
default:
fmt.Println("working...")
time.Sleep(1 * time.Second)
}
}
}
调用方
ctx, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(3 * time.Second)
cancel() // 3 秒后取消任务
}()
err := worker(ctx)
fmt.Println("worker exit:", err)
- 说明
worker 监听 ctx.Done()
cancel() 一调用
所有监听 ctx 的 goroutine 同时收到退出信号
3 场景 2:判断“任务是否正常完成 vs 被取消”
正确模式:任务返回结果
func job(ctx context.Context) error {
for i := 0; i < 5; i++ {
select {
case <-ctx.Done():
return ctx.Err()
default:
time.Sleep(1 * time.Second)
}
}
return nil // 正常完成
}
调用
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
err := job(ctx)
if err == nil {
fmt.Println("任务正常完成")
} else {
fmt.Println("任务被中断:", err)
}
结论
返回值 含义
nil 任 务完成
context.Canceled 被取消
context.DeadlineExceeded 超时
4 最常见场景 :ctx + goroutine + select
func runTask(ctx context.Context) {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
fmt.Println("task stop:", ctx.Err())
return
case <-ticker.C:
fmt.Println("task running")
}
}
}
ctx, cancel := context.WithCancel(context.Background())
go runTask(ctx)
time.Sleep(5 * time.Second)
cancel()
5 场景 4:任务完成后主动结束 ctx
关键:cancel 是由创建 ctx 的地方持有的
ctx, cancel := context.WithCancel(context.Background())
go func() {
for i := 0; i < 10; i++ {
time.Sleep(1 * time.Second)
}
fmt.Println("task finished")
cancel() // 通知所有监听者
}()
监听方:
<-ctx.Done()
fmt.Println("ctx done:", ctx.Err())
这就是 “任务完成 → 通知外部” 的标准写法。
6 工程里最推荐场景 :ctx + channel
ctx 管生死,channel 管结果
type Result struct {
Count int
}
func job(ctx context.Context, resultCh chan<- Result) {
defer close(resultCh)
count := 0
for {
select {
case <-ctx.Done():
return
default:
time.Sleep(500 * time.Millisecond)
count++
if count == 10 {
resultCh <- Result{Count: count}
return
}
}
}
}
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
resultCh := make(chan Result)
go job(ctx, resultCh)
select {
case res := <-resultCh:
fmt.Println("任务完成:", res.Count)
case <-ctx.Done():
fmt.Println("任务失败:", ctx.Err())
}
反例(一定不要这样)
ctx.Done() <- struct{}{} // ❌ 错
ctx = context.WithValue(ctx, “done”, true) // ❌ 滥用
time.Sleep(10 * time.Second) // ❌ 不可取消
最重要的工程口诀(建议背下来)
√ ctx:控制生命周期
√ channel:传递结果 / 状态
❌ ctx:不是消息队列
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)