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

学习笔记-Golang中的Context

文章目录

    • 1、什么是Context
    • 2、Context的作用
    • 3、Context的解析
      • 3.1、Context的源码解析
      • 3.2、 context包中实现context接口的四种结构体类型
      • 3.2.1、emptyCtx
      • 3.2.2、cancelCtx
      • 3.2.3、timerCtx
      • 3.2.4、valueCtx
    • 4、总结

1、什么是Context

Context是 Go 语言中的一个标准库,用于在处理并发编程时,提供对请求的截止时间取消信号以及请求范围内共享数据的管理。它在多个 goroutine 之间传递,是一种上下文管理工具。

Context 的主要目的是在不同的 goroutine 之间传递控制信号和数据。通过 Context,你可以:

  • 设置请求的超时时间或截止时间。
  • 传递取消信号,以便可以通知 goroutine 任务已经完成或需要停止。
  • 在多个 goroutine 之间共享值。

2、Context的作用

设想这样一种场景:你正在开发一个 Web 应用,当用户发送一个请求(request)后,可能会启动多个 goroutine 来处理该请求的不同部分。如果单个请求的 goroutine 结构比较简单,处理起来也不麻烦,但是如果启动多个、深层次的 goroutine,则会导致代码的复杂性和可维护性变得更加困难。而有了context,我们就可以通过它来管理这些 goroutine,并在它们之间传递控制信号和数据。

Context 在这种场景中的作用主要体现在以下几个方面:

  1. 取消信号传递:处理用户请求的过程中,可能会因为用户主动取消请求或请求超时而需要提前停止某些 goroutine 的执行。Context 提供了一种机制,可以在多个 goroutine 之间传递取消信号,当父 Context 取消时,所有与之关联的子 goroutine 都会收到取消通知并可以安全地停止工作,避免不必要的资源消耗。
  2. 数据共享:在处理用户请求时,某些全局信息(例如用户认证信息、追踪 ID 等)需要在不同的 goroutine 之间共享。通过 Context,这些数据可以被安全地传递,而不需要通过全局变量或其他不安全的方式共享。这样可以保持代码的简洁性和安全性,尤其在并发环境下避免数据竞争。
  3. 超时控制:在处理用户请求时,可能会遇到一些耗时长的操作,例如数据库查询或网络请求。Context 提供了一种机制,可以设置超时时间,当超时时间到达后,Context 会自动取消相关的 goroutine,避免资源占用过多。

除了上述作用外,Context 还可以更高效地管理系统资源,例如在请求超时或任务完成时主动释放资源,避免资源浪费,防止 goroutine 泄漏。

3、Context的解析

3.1、Context的源码解析

Context 实际上是一个定义了4个方法的接口,凡是实现了该接口的都称为一种 Context。

type Context interface {// 返回 context 的截止时间(deadline),表示任务应该在什么时候之前完成。// 如果没有设置截止时间,就返回 false 和一个空的时间值。Deadline() (deadline time.Time, ok bool)//当 context 被取消或者超时时,这个方法返回的 channel 会被关闭。通过监听这个信号,可以决定是否要终止正在进行的任务。Done() <-chan struct{}//这个方法用来返回 context 被取消的具体原因,比如是因为超时、手动取消等,通常会在 Done() 关闭后使用。Err() error// 从 context 中获取与某个 key 关联的数据,用于在 goroutine 之间传递一些共享信息。// key 可以是任何类型,用于区分不同的数据。Value(key interface{}) interface{}
}

3.2、 context包中实现context接口的四种结构体类型

context包中,有四个结构体类型实现了Context接口,分别是emptyCtxcancelCtxtimerCtxvalueCtx

3.2.1、emptyCtx

emptyCtx 是一个没有任何取消、截止时间或值的 Context,它通常作为默认的上下文,在创建顶级 Context 时使用。

解释:
emptyCtx 实现了 Context 接口的所有方法,但它们都返回默认值或 nil,因此 emptyCtx`是没有实际功能的。那么它存在的目的是什么呢?

原因是,在 Go 的 Context 体系中,Context 对象通常是分层次的,即通过创建一个父 Context 来衍生出子 Context,并在这些子 Context 中传递控制信号、共享数据等。因此,为了构建这棵 Context 对象树,我们需要一个基础的、没有任何附加信息的根节点。emptyCtx 正是为了提供这样一个基本的起点。
此外,有时你在编写代码时需要一个 Context 占位符,但暂时不确定具体的 Context 类型。这时你可以使用 context.TODO(),它返回的实际上也是 emptyCtx。这样可以让代码先运行,稍后再决定具体的 Context 实现。

总之,emptyCtx 是一个没有任何功能的 Context,它的使用场景有两个:

  1. 用于顶层上下文,作为 Context 对象树的根节点。
  2. 作为占位符,用于在编写代码时暂时不确定具体的 Context 类型。

注意:这四种结构体中,只有emptyCtx不需要通过已有context实例创建,因此一个context树的根context一定是emptyCtx

使用示例:

ctx := context.Background()
// 或者
ctx := context.TODO()
// 以下代码中,ctx 就是一个空的上下文

3.2.2、cancelCtx

cancelCtx 是带有取消功能的 Context,它允许父 Context 取消,并通知所有子 Context 和相关 goroutine。

解释:

  • cancelCtx 通过 WithCancel 函数创建,提供一种手动取消 Context 的机制。当调用 cancel() 函数时,所有依赖此 Context 的子 goroutine 都会收到取消通知。
  • Done() 方法会返回一个关闭的 channel,表示 Context 已经被取消。

使用场景:

  1. 用于主动取消某个操作,比如超时、用户主动取消等。
  2. 适用于请求处理被取消、提前终止任务等场景。

使用示例:

package mainimport ("context""fmt""time"
)func main() {// 通过emptyCtx创建一个带有取消功能的上下文(context.Background()创建的即为emptyCtx)ctx, cancel := context.WithCancel(context.Background())go func() {// 模拟执行任务for {select {case <-ctx.Done(): // 接收到取消信号,退出 goroutinefmt.Println("Task canceled")returndefault:fmt.Println("Task running...")time.Sleep(1 * time.Second)}}}()// 运行 3 秒后取消操作time.Sleep(3 * time.Second)cancel()// 给 goroutine 充分的时间来处理取消操作time.Sleep(1 * time.Second)
}

注意:当 Done() 信号触发时,虽然上下文 Context 已经被取消,但具体的 goroutine 或子进程是否终止,取决于代码逻辑。cancel() 只是通知 goroutine Context 已被取消,具体的退出逻辑需要我们手动处理。

3.2.3、timerCtx

timerCtx 是带有超时或截止时间(Deadline)的 Context,当超过指定时间时,Context 会自动取消。

解释:

  • timerCtx 是通过 WithTimeoutWithDeadline 创建的。它允许你为 Context 设置一个时间限制,当超时或达到截止时间时,Context 会自动取消,所有关联的 goroutine 都会收到取消通知。
  • 主要用于那些有执行时间限制的任务。

使用场景:

  1. 当你需要控制某个任务的最大执行时间(如网络请求、数据库操作等),防止任务无限期地运行时,使用 timerCtx。
  2. 适用于设置请求超时、操作超时等场景。

使用示例:

// // 通过emptyCtx创建一个带有取消功能的上下文(context.Background()创建的即为emptyCtx)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()select {
case <-time.After(3 * time.Second):fmt.Println("Task finished")
case <-ctx.Done():fmt.Println("Timeout:", ctx.Err()) // 这里会输出超时的错误
}

3.2.4、valueCtx

valueCtx 是可以在 goroutine 之间传递键值对的 Context,用于携带与请求相关的信息。context.WithValue() 可以将特定的值与某个 key 关联起来,并且这个 key 通常是一个自定义类型,而不是直接使用内置的 string 或 int 类型。

解释:
valueCtx 是通过 WithValue 创建的,用于在不同的 goroutine 之间传递一些与上下文相关的数据。这个数据可以通过 Context 的Value()方法获取。 context.WithValue() 可以将特定的值与某个 key 关联起来,并且这个 key 通常是一个自定义类型,而不是直接使用内置的数据类型,如stringint 类型。

使用场景:

  1. 适用于在多个 goroutine 之间传递一些共享信息,如用户 ID、请求 ID 等。
  2. 避免使用全局变量或复杂的依赖传递,确保上下文中的数据和 goroutine 的生命周期一致。

使用示例:

type key stringfunc main() {ctx := context.WithValue(context.Background(), key("userID"), 123)go func(ctx context.Context) {userID := ctx.Value(key("userID")) // 定义了一个新的类型 key,底层类型为 stringfmt.Println("User ID:", userID)}(ctx)time.Sleep(1 * time.Second)
}

注意:在上述代码中,如果直接使用 string 类型作为 key ,很可能会出现 key 重名的情况。比如,不同的模块可能会不小心使用相同的字符串 “userID” 作为 key ,这会导致冲突。因此,为了避免这种情况,我们会创建一个自定义类型的 key ,比如 type key string 。这样,尽管它本质上仍然是字符串类型,但它已经是一个新的类型,这样就避免了潜在的冲突问题。

4、总结

Context 是 Go 语言中的一个标准库,用于在处理并发编程时,提供对请求的截止时间、取消信号以及请求范围内共享数据管理的管理。它在多个 goroutine 之间传递,是一种上下文管理工具。

通过context包给出的四种结构体类型,我们可以轻松实现对 goroutine 的管理:

  • emptyCtx:基础的空 Context,没有取消、超时或值,适合作为顶层 Context 使用。
  • cancelCtx:提供手动取消功能,适合需要手动控制任务生命周期的场景。
  • timerCtx:带有超时或截止时间,适合限制任务执行时间的场景。
  • valueCtx:允许传递数据,适合跨 goroutine 传递少量与请求相关的上下文信息。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • (算法)大数的进制转换
  • 【Webpack--000】了解Webpack
  • linux + 宝塔 + django + websocket 部署
  • 【C++前后缀分解】1888. 使二进制字符串字符交替的最少反转次数|2005
  • Docker部署Joplin Server教程
  • 嵌入式开发—CAN通信协议详解与应用(中)
  • Java 线程超时时间:作用于核心线程还是最大线程?
  • libyuv之linux编译
  • 【揭秘Java】线程安全中的有序性之谜
  • 信通院发布首个《大模型媒体生产与处理》标准,阿里云智能媒体服务作为业界首家“卓越级”通过
  • 树莓派智能语音助手实现音乐播放
  • TSRPC+Cocos
  • 在react中 使用redux
  • 2024年最新软件测试学习路线图(从入门到精通)
  • 利用长度选择器优化Prompt示例选择:提升AI对话效率
  • JS中 map, filter, some, every, forEach, for in, for of 用法总结
  • 实现windows 窗体的自己画,网上摘抄的,学习了
  • [LeetCode] Wiggle Sort
  • [Vue CLI 3] 配置解析之 css.extract
  • Android Studio:GIT提交项目到远程仓库
  • css布局,左右固定中间自适应实现
  • express.js的介绍及使用
  • hadoop集群管理系统搭建规划说明
  • JavaWeb(学习笔记二)
  • java取消线程实例
  • node-sass 安装卡在 node scripts/install.js 解决办法
  • 工程优化暨babel升级小记
  • 简单基于spring的redis配置(单机和集群模式)
  • 说说动画卡顿的解决方案
  • 异步
  • 字符串匹配基础上
  • 1.Ext JS 建立web开发工程
  • 说说我为什么看好Spring Cloud Alibaba
  • ​​​​​​​开发面试“八股文”:助力还是阻力?
  • ​Linux Ubuntu环境下使用docker构建spark运行环境(超级详细)
  • ​无人机石油管道巡检方案新亮点:灵活准确又高效
  • #DBA杂记1
  • #WEB前端(HTML属性)
  • #经典论文 异质山坡的物理模型 2 有效导水率
  • #我与Java虚拟机的故事#连载03:面试过的百度,滴滴,快手都问了这些问题
  • $(document).ready(function(){}), $().ready(function(){})和$(function(){})三者区别
  • (1)svelte 教程:hello world
  • (1)虚拟机的安装与使用,linux系统安装
  • (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  • (html转换)StringEscapeUtils类的转义与反转义方法
  • (k8s)Kubernetes本地存储接入
  • (react踩过的坑)antd 如何同时获取一个select 的value和 label值
  • (TipsTricks)用客户端模板精简JavaScript代码
  • (动手学习深度学习)第13章 计算机视觉---图像增广与微调
  • (附源码)php投票系统 毕业设计 121500
  • (附源码)springboot教学评价 毕业设计 641310
  • (四十一)大数据实战——spark的yarn模式生产环境部署
  • (一)appium-desktop定位元素原理
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • ****Linux下Mysql的安装和配置