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

go-context创建及使用详细概括

一.为什么需要context

1.什么是context

Context(上下文)是Go语言中一个非常重要的概念,它主要用于在多个goroutine之间传递请求相关的数据、取消信号以及超时信息ContextGo 1.7版本中被正式引入标准库,现已成为处理并发控制和请求作用域数据的标准方式。

Context的核心思想是提供一种统一的方式来管理goroutine的生命周期,特别是在处理网络请求、RPC用或任何需要跨API边界传递截止时间和取消信号的场景中。

2.为什么需要context

在go的并发编程中,我们经常遇到以下问题?


1.如何优雅地取消goroutine:当一个操作不再需要时,如何通知相关的goroutine停止工作


2.如何设置操作超时:如何确保一个操作不会无限期地执行下去

    3.如何在调用链中传递请求作用域的值:如何在函数调用链中安全地传递请求特定的数据


二.Context基础接口和四个构造函数

1.context接口

  • Deadine():返回context的截止时间,如果未设置截止时间,则ok返回false
  • Done():返回一个channel,当Context被取消或超时时,该channel会被关闭。比如
  • Deadlinecontext,关闭原因可能是因为deadline,也可能提前被主动关闭。
  • Err():返回Context结束的原因,如果Context还未结束则返回nil
  • Value(key):获取与key关联的值,如果key不存在则返回nil

2.根context

context包中定义了一个空的context,名为emptyCtx,用于context的根节点,空的context只是简单地实现了Context,本身不包含任何值,仅用于其他context的父节点

1.context.Background():上下文默认值,其他所有上下文都应该从它衍生出来;

2.context.ToDo():应该仅在不确定应该使用哪种上下文时使用。

3.其他类型context

context包中实现Context接口的struct,除了emptyCtx还有cancelCtx、timerCtx

valueCtx三种,正是基于这三种context实例,实现了上述四种类型的context。

4.根context和其他context的联系

    1.Context是树形结构
在Go中,所有的Context都形成一棵树(Tree)context.Background()是这棵树的根节点(root)每调用一次WithCancel、WithTimeout、WithDeadline或WithValue,都会基于父Context创建一个新的子Context
2.传播规则

  • 取消信号是自上而下传播的父Context被取消,所有子Context都会被取消;子Context被取消,不会影响父Context。
  • 值(Value)是向上查找的调用ctx.Value(key)时,会从当前Context向上逐级查找;一直查到根Context为止


三.Context工作原理

为什么context是树形结构

1.顶层:context.Background()是“根”;

  •     每一层函数调用时,用WithCancel/WithTimeout/WithValue基于父级创建新的Context;
  •     这样一层层下去,就形成一棵上下层依附的Context树。

2.  每个函数拿到自己的ctx;
上层取消时,整个下游链条都能响应;


3.子层取消不会影响兄弟节点或父层;

为什么能“向子 Context 传递取消信号”?

 每个Context都保存对「父节点」的引l用。

  1. 当你调用context.WithCancel(parent)时,会创建一个新的cancelCtx:它保存了父context;同时,父context把它注册到自己的children集合里。
  2. 取消信号会递归向下传递,就像广播一样

为什么 Value 查询是“自下往上”

1.它把key/value存在自己的结构体里,并且保存了父Context的引用。


2.当调用时,如果当前结点没有就递归去父节点去找


3.子Context是"局部上下文",可以覆盖父Context的值;


四.Context使用场景

取消信号场景

for {//等待下一个连接到该接口的连接accept, err2 := listen.Accept()if err2 != nil {log.Println("客户端连接失败:", err2)break}fmt.Printf("连接成功对应的ip地址,端口号:%v\n", accept.RemoteAddr().String())// 为每个客户端创建一个独立的 context 控制生命周期ctx, cancel := context.WithCancel(context.Background())client := &method.Client{Conn:     accept,Nickname: "",Boo:      true,}//为每个用户开启广播协程go method.Radio(ctx)//开启登录功能go func() {err = client.LoginOrCreate()if err != nil {log.Println("登录注册功能", err)cancel()}//开启信息处理流程err = client.SRead()if err != nil {log.Println("消息处理功能", err)cancel()}}()//开启队列读取消息功能go redis.ReceiveRealtimeMessage()}

如果在消息处理,或者登录出现错误时,就发送取消信号,则广播协程接受到取消信号时就会关闭

1.redis消息队列中取出一个消息,并传入通道里被处理

2.当有取消信号传来时,就不会执行处理任务函数

超时停止信号

func doWork(ctx context.Context) {select {case <-time.After(2 * time.Second): // 模拟任务需要 2s 才能完成fmt.Println("✅ work finished")case <-ctx.Done(): // 超时或被取消fmt.Println("❌ work canceled:", ctx.Err())}
}func main() {ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)defer cancel() // 避免资源泄漏doWork(ctx)fmt.Println("Main exit")
}

1.可以来限制任务超时时,取消任务

在协程之间传递小量元数据

func main() {const RequestID key = "request-id"// 创建带有 request-id 的 Contextctx := context.WithValue(context.Background(), RequestID, "REQ-12345")go func(ctx context.Context) {id := ctx.Value(RequestID).(string)fmt.Println("Worker got RequestID:", id)}(ctx)time.Sleep(time.Second) // 等待输入避免主程序退出
}

http数据库的使用

http

✔ 控制请求超时

控制请求取消

传递链路元信息(trace id

在服务端自动中止处理逻辑(例如客户端断开连接)

数据库

✔ 控制 SQL 查询超时

取消执行中的 SQL

防止慢查询拖垮业务

传播请求链路信息(如 trace


五.Context常见问题和注意事项

1.忘记调用cancel()

goroutine退出,cancelCtx的内部资源(如 done channel、timer)不会立刻释放。
后果:

  1. 内存泄漏
  2. 定时器(WithTimeout/WithDeadline)泄漏;
  3. 子context无法正确清理

解决方法:始终在创建后立即defercancel()

2. 滥用 context.Background() context.TODO()

在业务函数、HTTP handler或 service 层随意创建新的 context.Background(),导致:

  •    上下游取消信号丢失;
  •     日志链路追踪丢失(tracelD不传递);
  •     不可控的goroutine无法终止。

    解决方案:
1.应总是从上层传入的Context派生新的Context:
2.不要随意"断开”Context链。

3. 在结构体或全局变量中长期保存 Context

问题

Context 是请求作用域的临时对象,生命周期通常在一次调用内。

若把它保存在结构体或全局变量中,会导致:

1.数据竞争;

2.过期 Context 误用

3.不可控的取消或超时传播。

解决方案

1.Context 应作为函数参数传递;

2.永远不要存入全局或结构体中;

3.如果一定要有长期 Context,可用 context.Background() 在程序启动时初始化全局根 Context。

补充:

如果你的程序中

大量短生命周期的小对象高频创建、销毁(如 bytes.Buffer[]bytecontext 包装结构等)高并发触发那么会频繁分配内存,性能会劣化非常明显

所以 Go 官方提供 sync.Poolsync.Pool 用来缓存“暂时不用但未来可能再用”的对象。

4. 滥用 context.Value

问题:
很多人把Context当作“万能存储”,往里塞业务数据:
ctx := context.WithValue(context.Background(), "user_id", 123)
结果:

    Context的作用被滥用;

    性能变差(Value查找是链式查找);

    逻辑混乱(业务数据不应藏在Context)。

正确做法:

1.仅用于跨 API 边界传递元数据(如 traceID、请求ID、认证信息);

2.不要传递业务参数。

5. 忽略 ctx.Done() 导致 goroutine 泄漏,Err()的使用

err()

🔹 问题

很多人不知道什么时候用 Err()

ctx.Err() 只有两种结果:context.Canceled    context.DeadlineExceeded

🔹 用法:select {case <-ctx.Done():    fmt.Println(ctx.Err()) // 输出取消原因}

可以用来区分手动取消和超时取消。

ctx.Done()

如果上层取消 Context,这个 goroutine 仍然在跑 —— 永远不会退出。

🔹 解决方案在循环中监听 <-ctx.Done()

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

相关文章:

  • go进阶学习
  • 做网站建设培训wordpress如何添加网站地图
  • 网站关键词搜索排名优化郑州建设网站定制
  • Java输入输出:编程世界的入口和出口
  • Xcode编译C语言:提升编译效率与调试技巧
  • MONGO-EXPRESS Docker 容器化部署指南
  • 免费psd图片素材网站邯郸网站开发
  • IDEA配置Maven
  • 昆明做网站外包预定型网站有哪些
  • 深圳建设工程交易中心网站百度链接提交地址
  • 《中医基础理论》- 2.哲学基础之藏象学说-肾系统详解
  • 绍兴网站建设方案响应式网站建设报价单
  • 采用Langchain调用LLM完成简单翻译任务
  • 游戏网站建设流程图注册一个电商公司需要多少钱
  • 深度拥抱变革:AI 重塑临床工作流与医院信息化的战略蓝图与实施路线
  • 北京网站制作公司招聘淘宝客网站备案
  • 服务器开荒:安装宝塔面板
  • 基于AWS的应用程序可靠性提升架构优化方案——RDS多可用区与EC2弹性架构实践
  • 数据库事务ACID特性详解
  • 基于单片机的自行车速度与里程检测报警系统设计
  • 网站运营计划书建筑费用明细表模板
  • GPT-5.1发布!你的AI更暖更智能!
  • 万悉科技GEO专题分享会——共探AI时代中国出海企业的流量新机遇
  • 商业网站的相关内容青岛网站设计网站
  • Python判断字符串中是否有中文
  • 前端动画技术发展方向
  • 图片网站该如何做seo优化万户网络做网站怎么样
  • qt显示类控件---QLCDNumber
  • C#1115变量
  • 钓鱼网站排名假冒建设银行最多wordpress停止更新