当前位置: 首页 > news >正文

Go语言实战案例:使用context控制协程取消

在并发编程中,合理地控制协程(goroutine)的生命周期是保证程序稳定性和资源可控使用的关键。Go语言标准库中的 context 包正是为了解决这一问题而生。它为我们提供了取消信号、超时控制、请求作用域的值传递等功能。

本文将通过一个实际案例,演示如何使用 context 控制协程的取消,避免资源泄露,实现优雅退出。


一、什么是 context

context 是 Go 1.7 起加入标准库的一个重要包,用于跨 API 边界传递取消信号、超时时间、截止时间等信息。

主要接口定义如下:

type Context interface {Deadline() (deadline time.Time, ok bool)Done() <-chan struct{}Err() errorValue(key any) any
}

其中最关键的是:

  • • Done():返回一个 channel,当 context 被取消或超时关闭时,该 channel 会被关闭;
  • • Err():返回取消的原因,例如 context.Canceled 或 context.DeadlineExceeded

二、常见创建方式

Go 提供了以下常用方式创建 context:

ctx := context.Background() // 最顶层、永不取消的 context
ctx, cancel := context.WithCancel(parent) // 手动调用 cancel() 取消
ctx, cancel := context.WithTimeout(parent, 3*time.Second) // 指定超时时间
ctx, cancel := context.WithDeadline(parent, time.Now().Add(3*time.Second)) // 到期时间点

这些 context 都可以传递到协程中,通过 ctx.Done() 控制协程的停止。


三、实战案例:使用 context 控制任务协程

场景描述

假设我们要运行一个任务,该任务每秒输出一次“正在处理”,但当主程序在某个时机需要终止它(比如点击“停止按钮”或超时),我们要优雅地通知协程退出。

示例代码

package mainimport ("context""fmt""time"
)func worker(ctx context.Context) {for {select {case <-ctx.Done():fmt.Println("worker 任务被取消:", ctx.Err())returndefault:fmt.Println("worker 正在处理任务...")time.Sleep(1 * time.Second)}}
}func main() {ctx, cancel := context.WithCancel(context.Background())go worker(ctx)// 主线程运行5秒后取消任务time.Sleep(5 * time.Second)cancel()// 等待协程打印结束语time.Sleep(1 * time.Second)fmt.Println("主程序退出")
}

输出结果

worker 正在处理任务...
worker 正在处理任务...
worker 正在处理任务...
worker 正在处理任务...
worker 正在处理任务...
worker 任务被取消: context canceled
主程序退出

可以看到,主程序通过 cancel() 取消了 context,协程立即响应退出了。


四、使用 context.WithTimeout 实现超时控制

除了手动调用 cancel,我们还可以通过设定超时时间自动取消任务。

func main() {ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)defer cancel()go worker(ctx)time.Sleep(5 * time.Second) // 主线程等待更久,看任务是否能自动终止fmt.Println("主程序退出")
}

运行结果类似:

worker 正在处理任务...
worker 正在处理任务...
worker 正在处理任务...
worker 任务被取消: context deadline exceeded
主程序退出

这表示:即使主程序没有主动调用 cancel(),协程也在 3 秒后收到 context 超时通知并正常退出。


五、多个协程共享一个 context

我们可以启动多个协程,并使用同一个 context 控制它们:

func main() {ctx, cancel := context.WithCancel(context.Background())for i := 1; i <= 3; i++ {go func(id int) {for {select {case <-ctx.Done():fmt.Printf("协程 %d 接收到取消信号\n", id)returndefault:fmt.Printf("协程 %d 正在工作\n", id)time.Sleep(1 * time.Second)}}}(i)}time.Sleep(4 * time.Second)cancel()time.Sleep(1 * time.Second)fmt.Println("主程序退出")
}

输出:

协程 1 正在工作
协程 2 正在工作
协程 3 正在工作
...(多次输出)
协程 1 接收到取消信号
协程 2 接收到取消信号
协程 3 接收到取消信号
主程序退出

这样我们实现了“一键终止所有协程”。


六、最佳实践建议

  • • 永远使用 context.WithCancel / WithTimeout 返回的 cancel() 函数,不要忘记 defer cancel()
  • • 在需要可控中止的任务(如网络请求、数据库操作、循环处理)中传入 context;
  • • 对 ctx.Done() 的监听要放在协程内部适当位置,防止资源泄露;
  • • 在请求链中传递 context,以实现链路级别的取消与超时控制。

七、结语

context 是 Go 并发控制中不可或缺的利器。它不仅解决了协程取消的痛点,还能为任务设置统一的生命周期控制逻辑,是构建高可靠网络服务、后台任务系统、爬虫等并发程序的基础设施。

如果你还没有在项目中大量使用它,不妨从今天开始重构代码,使用 context 管理协程生命周期,让你的 Go 程序更稳定、更可控!

http://www.dtcms.com/a/316140.html

相关文章:

  • GB28181监控平台LiveGBS如何配置GB28181对接海康、大华解码器上墙,将GB28181平台是视频给硬件解码器解码上墙
  • 软件无线电 招标参数
  • ⭐CVPR2025 非均匀运动视频插帧新突破
  • 文献阅读 | Briefings in Bioinformatics | Hiplot:全面且易于使用的生物医学可视化分析平台
  • HarmonyOS 应用拉起系列(二):如何拉起微信小程序
  • 前端1.0
  • 查看 Redis 某个数据库的内存占用
  • python+MySQL组合实现生成销售财务报告
  • 站在前端的角度,看鸿蒙页面布局
  • MTK-Android 系统拷贝预置资源
  • 本地使用uv管理的python项目怎么部署到服务器?
  • Next.js 链接与导航:页面间无缝切换
  • 最新安卓原生对接苹果cms App后端+app(最新优化版)
  • Spring Cloud系列—简介
  • 从循环嵌套到拓扑编排:LangGraph如何重构Agent工作流
  • 网络 —— 笔记本(主机)、主机虚拟机(Windows、Ubuntu)、手机(笔记本热点),三者进行相互ping通
  • 企业AI转型之战:Coze、Dify与FastGPT的巅峰对决
  • css动态样式
  • Linux 内存管理之 Rmap 反向映射(二)
  • 去哪儿StarRocks实践
  • 以Linux为例补充内存管理基础知识
  • 【 IPMI 内核模块】重新加载
  • BeeWorks私有化即时通讯,局域网办公安全可控
  • 光伏电站环境监测系统:绿色能源的“智慧守护者”
  • 是的,或许这就是意识!
  • 政安晨【开源人工智能硬件】【ESP乐鑫篇】 —— 详细分享小智(78/xiaozhi-esp32)AI终端开源硬件的嵌入式开发经验笔记
  • C语言---文件操作
  • 上传文件至华为云OBS
  • 分布式微服务--Nacos 集群部署
  • 【CTF】命令注入绕过技术专题:变量比较与逻辑运算