标准库之context(很重要)
1|0标准库之context
- 在 Go的http包的Server端中,每一个请求在都有一个对应的
goroutine
去处理。请求处理函数通常会启动额外的goroutine
用来访问后端服务,比如数据库和RPC服务。用来处理一个请求的goroutine
通常需要访问一些与请求特定的数据,比如终端用户的身份认证信息、验证相关的token、请求的截止时间。 当一个请求被取消或超时时,所有用来处理该请求的goroutine
都应该迅速退出,然后系统才能释放这些goroutine
占用的资源。
2|0一、为什么需要context
- 可以先挨个看看下面的几个并发示例的不足之处
2|11. 基本并发的示例
- 本例中,我们只能老老实实的等待子
goroutine
完成后,才能继续主goroutine
的执行。无法手动去结束子goroutine
2|22. 全局变量方式的并发
- 使用一个全局变量,当该全局变量作为参数进入多个
goroutine
的任务中时,或者跨包时就变得不好控制了。 - 同样,还存在上面基本并发中无法手动控制子
goroutine
的退出
2|33. 通道方式的并发
- 采用
channel
作为多个goroutine
之间的数据交互的媒介,是实现了数据安全的交互,但道理同上面的全局变量一样,当跨包时,或者子goroutine
中继续启动goroutine
时,需要维护一个共用的channel
,也不太友好。 - 同样,还存在上面基本并发中无法手动控制子
goroutine
的退出
2|44. 官方版的方案
- 下面是官方给出的解决方案的参考案例:使用
context
包进行上下文的管理
- 当子
goroutine
又开启另外一个goroutine
时,只需要将ctx传入即可:
3|0二、Context初识
-
Go1.7版本加入了一个新的标准库
context
,它定义了Context
类型,专门用来简化 对于处理单个请求的多个goroutine
之间与请求域的数据、取消信号、截止时间等相关操作,这些操作可能涉及多个 API 调用。 -
对服务器传入的请求应该创建上下文,而对服务器的传出调用应该接受上下文。它们之间的函数调用链必须传递上下文,或者可以使用
WithCancel
、WithDeadline
、WithTimeout
或WithValue
创建的派生上下文。当一个上下文被取消时,它派生的所有上下文也被取消。
3|11. Context接口
context.Context
是一个接口,该接口定义了四个需要实现的方法。具体签名如下:
其中:
Deadline
方法需要返回当前Context
被取消的时间,也就是完成工作的截止时间(deadline)Done
方法需要返回一个Channel
,这个Channel会在当前工作完成或者上下文被取消之后关闭,多次调用Done
方法会返回同一个Channel
Err
方法会返回当前Context
结束的原因,它只会在Done
返回的Channel被关闭时才会返回非空的值- 如果当前
Context
被取消就会返回Canceled
错误 - 如果当前
Context
超时就会返回DeadlineExceeded
错误
- 如果当前
Value
方法会从Context
中返回键对应的值,对于同一个上下文来说,多次调用Value
并传入相同的Key
会返回相同的结果,该方法仅用于传递跨API和进程间跟请求域的数据
3|22. 两个顶级Context
- context包提供两种顶级的上下文类型
Background()
和TODO()
,这两个内置的上下文对象作为最顶层的partent context
,衍生出更多的子上下文对象
(1)Background()和TODO()
-
context.Background()
主要用于main函数、初始化以及测试代码中,作为Context这个树结构的最顶层的Context,也就是根Context。 -
context.TODO()
返回非零的空上下文。当不清楚要使用哪个上下文或者它还不可用时,应该使用context.TODO()
(2)区别
- 本质上都是
emptyCtx
结构体类型,其源码实现是一样的,是一个不可取消,没有设置截止时间,没有携带任何值的Context。只不过使用场景不同,context.Background()
通常由主函数、初始化和测试使用,是顶级Context;context.TODO()
通常用于主协程外的其他协程向下传递,分析工具可识别它在调用栈中传播
3|33. 派生Context(With系列函数)
- 此外,
context
包中还定义了四个With系列函数。
(1)WithCancel(可取消context)
-
用于创建一个具有取消功能的上下文(context)。它的主要目的是允许你在需要的时候取消一个长时间运行的任务或多个任务,以确保程序可以优雅地退出或处理超时情况
-
WithCancel
的函数定义如下:
-
WithCancel
返回带有新Done通道的父节点的副本。当调用返回的cancel函数或当关闭父上下文的Done通道时,将关闭返回上下文的Done通道,无论先发生哪种情况。 -
取消此上下文将释放与其关联的资源,因此代码应该在此上下文中运行的操作完成后立即调用cancel。
i. context控制goroutine内部的goroutine的停止
ii. context控制多个goroutine的停止
(2)WithDeadline(超时取消context)
-
WithDeadline
用于创建一个到达指定时间后能被自动取消的上下文 -
WithDeadline
的函数签名如下:
-
WithDeadline
函数返回父上下文的副本,其截止时间调整为不迟于d。如果父上下文的截止时间早于d,则WithDeadline(Parent,d)
在语义上等同于父上下文。 -
当截止时间到期、调用返回的
cancel
函数或关闭父上下文的done通道(以先发生者为准)时,返回的上下文的done通道将关闭(即当设定的延迟时间到了之后,或者未到延迟时间时主动调用cancel
函数,或关闭父上下文的done通道,就关闭上下文,以最先发生的情况为准)。 -
取消此上下文将释放与其关联的资源,因此代码应该在此上下文中运行的操作完成后立即调用
cancel
。
(3)WithTimeout(超时取消context)
-
WithTimeout
方法用于创建一个经过一段时间后能被自动取消的上下文,他也是调用WithDeadline
这个方法 -
WithTimeout
的函数签名如下:
-
WithTimeout
返回WithDeadline(parent, time.Now().Add(timeout))
。 -
取消此上下文将释放与其相关的资源,因此代码应该在此上下文中运行的操作完成后立即调用cancel,通常用于数据库或者网络连接的超时控制。具体示例如下:
(4)WithValue(向context添加值)
WithValue
函数能够将请求作用域的数据与 Context 对象建立关系。声明如下:
-
WithValue
返回父节点的副本,其中与key关联的值为val。 -
仅对API和进程间传递请求域的数据使用上下文值,而不是使用它来传递可选参数给函数。
-
所提供的键必须是可比较的,并且不应该是
string
类型或任何其他内置类型,以避免使用上下文在包之间发生冲突。WithValue
的用户应该为键定义自己的类型。为了避免在分配给interface{}时进行分配,上下文键通常具有具体类型struct{}
。或者,导出的上下文关键变量的静态类型应该是指针或接口。
4|0三、使用Context的注意事项
- 推荐以参数的方式显示传递Context
- 以Context作为参数的函数方法,应该把Context作为第一个参数。
- 给一个函数方法传递Context的时候,不要传递nil,如果不知道传递什么,就使用
context.TODO()
- Context的Value相关方法应该传递请求域的必要数据,不应该用于传递可选参数
- Context是线程安全的,可以放心的在多个
goroutine
中传递
5|0四、Context常见示例
5|11. 控制10s后,所有协程退出
- 使用
context
包来实现线程安全退出或超时的控制:控制10s后,所有协程退出
5|22. 控制某个go协程执行5次就结束
5|33. 打印100个素数
- Go语言是带内存自动回收特性的,因此内存一般不会泄漏。当
main
函数不再使用管道时后台Goroutine有泄漏的风险。我们可以通过context
包来避免这个问题,下面是防止内存泄露的素数筛实现:
6|0五、客户端超时取消示例
- 调用服务端API时如何在客户端实现超时控制?
6|11. server端
6|22. client端
__EOF__

本文链接:https://www.cnblogs.com/Mcoming/p/18073098.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构