Golang的context
目录
context的基本使用
为什么需要context
Context interface
标准 error
emptyCtx
cancelCtx
Deadline 方法
Done 方法
Err 方法
Value 方法
context.WithCancel()
newCancelCtx
WithCancel中propagateCancel
cancel
timerCtx
valueCtx
context的基本使用
创建:
context.Background():创建一个空的父类context
context.TODO(): 创建一个未来 可能有内容的父类 context,有扩展性
// 创建一个可以取消的ContextctxCancel, cancelCancel := context.WithCancel(context.Background())defer cancelCancel() // 当不再需要时,确保调用cancel来释放资源// 创建一个带有截止时间的Contextdeadline := time.Now().Add(time.Second * 10)ctxDeadline, cancelDeadline := context.WithDeadline(context.Background(), deadline)defer cancelDeadline()// 创建一个带有超时时间的Contexttimeout := 10 * time.SecondctxTimeout, cancelTimeout := context.WithTimeout(context.Background(), timeout)defer cancelTimeout()
返回值: Context 上下文本体 和 CancelFunc 关闭Context 的函数
类别:
context.WithCancel 创建一个可以取消的Context,当调用 返回值 cancel 时 就可以通知关闭
context.WithDeadline 创建一个带有截止时间的Context,到一个特定时间时便发出通知
context.WithTimeout 创建一个带有超时时间的Context,过了一段时间后就发出通知
防止go协程泄漏使用例子
package mainimport ("context""fmt"
)func main() {//WithCancel(ctx Context, cancel CancelFunc)=(名 Context,处理函数 CancelFunc)ctx, cancel := context.WithCancel(context.Background()) //context.Background() 处理 Goroutinecontext.TODO()ch := func(ctx context.Context) <-chan int {ch := make(chan int)go func() {for i := 0; ; i++ {select {case <-ctx.Done():returncase ch <- i:}}}()return ch}(ctx)for v := range ch {fmt.Println(v)if v == 5 {cancel()break}}
}
为什么需要context
场景1: 当主协程启动了m个子协程,m个子协程又启动更多的协程,
那监控起来需要很多的channel, 操作非常繁琐。
如果我们使用 context 时,当父类的context关闭,子类也会一起关闭以此类推,类似Qt中的对象树
场景2:任务A 挂在 任务B 下,我们希望 B 有着定时退出的功能,而且当B退出时A也需要退出
使用定时器+channel时,就显的有些繁琐,我们可以直接使用context.WithTimeout 一步到位
Context interface
golang的接口 类似 c++的基类
type Context interface {Deadline() (deadline time.Time, ok bool)Done() <-chan struct{}Err() errorValue(key any) any
}

Context 为 interface,定义了四个核心 api:
• Deadline:返回 context 的过期时间;
• Done:返回 context 中的 channel;
• Err:返回错误;
• Value:返回 context 中的对应 key 的值.
标准 error
var Canceled = errors.New("context canceled")var DeadlineExceeded error = deadlineExceededError{}type deadlineExceededError struct{}func (deadlineExceededError) Error() string { return "context deadline exceeded" }
func (deadlineExceededError) Timeout() bool { return true }
func (deadlineExceededError) Temporary() bool { return true
• Canceled:context 被 cancel 时会报此错误;
• DeadlineExceeded:context 超时时会报此错误.
emptyCtx
重写了接口 api:
type emptyCtx intfunc (*emptyCtx) Deadline() (deadline time.Time, ok bool) {return
}func (*emptyCtx) Done() <-chan struct{} {return nil
}func (*emptyCtx) Err() error {return nil
}func (*emptyCtx) Value(key any) any {return
}
返回的什么东西都为空
• emptyCtx 是一个空的 context,本质上类型为一个整型;
• Deadline 方法会返回一个公元元年时间以及 false 的 flag,标识当前 context 不存在过期时间;
• Done 方法返回一个 nil 值,用户无论往 nil 中写入或者读取数据,均会陷入阻塞;
• Err 方法返回的错误永远为 nil;
• Value 方法返回的 value 同样永远为 nil.
var (background = new(emptyCtx)todo = new(emptyCtx)
)func Background() Context {return background
}func TODO() Context {return todo
}
当使用context.Background() & context.TODO() 创建emptyCtx 时返回固定的 emptyCtx
cancelCtx
type cancelCtx struct {Contextmu sync.Mutex // protects following fieldsdone atomic.Value // of chan struct{}, created lazily, closed by first cancel callchildren map[canceler]struct{} // set to nil by the first cancel callerr error // set to non-nil by the first cancel call
}type canceler interface {cancel(removeFromParent bool, err error)Done() <-chan struct{}
}

Deadline 方法
cancelCtx 未实现该方法,仅是 embed 了一个带有 Deadline 方法的 Context interface,因此倘若直接调用会报错.
Done 方法
流程如下:
源码:
func (c *cancelCtx) Done() <-chan struct{} {d := c.done.Load()if d != nil {return d.(chan struct{})}c.mu.Lock()defer c.mu.Unlock()d = c.done.Load()if d == nil {d = make(chan struct{})c.done.Store(d)}return d.(chan struct{})
}
• 基于 atomic 包,读取 cancelCtx 中的 chan;倘若已存在,则直接返回;
• 加锁后,在此检查 chan 是否存在,若存在则返回;(双重检查 double check)
• 初始化 chan 存储到 aotmic.Value 当中,并返回.(懒加载机制 懒汉)
Err 方法
func (c *cancelCtx) Err() error {c.mu.Lock()err := c.errc.mu.Unlock()return err
}
• 加锁;
• 读取 cancelCtx.err;
• 解锁;
• 返回结果;
Value 方法
func (c *cancelCtx) Value(key any) any {if key == &cancelCtxKey {return c}return value(c.Context, key)
}
• 倘若 key 特定值 &cancelCtxKey,则返回 cancelCtx 自身的指针;
• 否则遵循 valueCtx 的思路取值返回;
context.WithCancel()
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {if parent == nil {panic("cannot create context from nil parent")}c := newCancelCtx(parent)propagateCancel(parent, &c)return &c, func() { c.cancel(true, Canceled) }
}
• 校验父 context 非空;
• 注入父 context 构造好一个新的 cancelCtx;
• 在 propagateCancel 方法内启动一个守护协程,以保证父 context 终止时,该 cancelCtx 也会被终止;
• 将 cancelCtx 返回,连带返回一个用以终止该 cancelCtx 的闭包函数.
newCancelCtx
没用propagateCancel 方法
func newCancelCtx(parent Context) cancelCtx {return cancelCtx{Context: parent}
}
• 注入父 context 后,返回一个新的 cancelCtx.
WithCancel中propagateCancel
propagateCancel 传播取消

func propagateCancel(parent Context, child canceler) {done := parent.Done()if done == nil {return // parent is never canceled}select {case <-done:// parent is already canceledchild.cancel(false, parent.Err())returndefault:}if p, ok := parentCancelCtx(parent); ok {p.mu.Lock()if p.err != nil {// parent has already been canceledchild.cancel(false, p.err)} else {if p.children == nil {p.children = make(map[canceler]struct{})}p.children[child] = struct{}{}}p.mu.Unlock()} else {atomic.AddInt32(&goroutines, +1)go func() {select {case <-parent.Done():child.cancel(false, parent.Err())case <-child.Done():}}()}
}
cancel
// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
// cancel sets c.cause to cause if this is the first time c is canceled.
func (c *cancelCtx) cancel(removeFromParent bool, err, cause error) {if err == nil {panic("context: internal error: missing cancel error")}if cause == nil {cause = err}c.mu.Lock()if c.err != nil {c.mu.Unlock()return // already canceled}c.err = errc.cause = caused, _ := c.done.Load().(chan struct{})if d == nil {c.done.Store(closedchan)} else {close(d)}for child := range c.children {// NOTE: acquiring the child's lock while holding parent's lock.child.cancel(false, err, cause)}c.children = nilc.mu.Unlock()if removeFromParent {removeChild(c.Context, c)}
}
timerCtx
只介绍类,其他与cancel差不多,加上了过期时刻
type timerCtx struct {cancelCtxtimer *time.Timer // Under cancelCtx.mu.deadline time.Time
}

valueCtx
键值对 Ctx
type valueCtx struct {Contextkey, val any
}

参考 : 小徐先生1212 --context 底层实现
相关文章:
Golang的context
目录 context的基本使用 为什么需要context Context interface 标准 error emptyCtx cancelCtx Deadline 方法 Done 方法 Err 方法 Value 方法 context.WithCancel() newCancelCtx WithCancel中propagateCancel cancel timerCtx valueCtx context的基本使用…...
Android 各个版本名称和特性总结(持续更新)
我们就从Android 5.0开始吧,因为从写文时起,大部分手机都到5.0了。 目录 Android5.0 (Lollipop 棒棒糖)新特性 Android6.0新特性 Android7.0新特性 Android8.0(O)新特性 Android9.0新特性 Android10.0(Q)新特性 Android11…...
9.0 Android中的网络技术
Android中网络相关的技术,主要分别两种,一种为直接显示网页,另外一种为获取服务器中的数据进行设置。 权限声明 访问网络是需要声明权限 <manifest xmlns:android"http://schemas.android.com/apk/res/android"package"…...
linux查看端口是否被占用 / 包含某个字符的文件/当前正在运行的进程/根据端口号查找进程
查看端口是否被占用 netstat -tuln | grep 80查看包含某个字符的文件 grep -rl "aaa" .r :递归搜索子目录。l :只显示包含匹配字符串的文件名。 ack "your_string"查看当前正在运行的进程 ps aux或者使用 top 命令用于实时显示当…...
解锁 JavaScript ES6:函数与对象的高级扩展功能
个人主页:学习前端的小z 个人专栏:JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结,欢迎大家在评论区交流讨论! ES5、ES6介绍 文章目录 💯ES6函数扩展🍓1 默认参数ἵ…...
算法金 | 10 大必知的自动化机器学习库(Python)
本文来源公众号“算法金”,仅用于学术分享,侵权删,干货满满。 原文链接:10 大必知的自动化机器学习库(Python) 一、入门级自动化机器学习库 1.1 Auto-Sklearn 简介: Auto-Sklearn 是一个自动…...
微信小游戏开发难度大吗?开发流程有哪些?
微信小游戏的开发难度因项目的复杂度和规模而定,一般来说,休闲益智类的小游戏的开发周期相对较短,大约在10个工作日到1个月。如果涉及到复杂的算法、高级的交互或特殊的效果,开发时间可能会相应延长。 微信小游戏的开发流程包括需…...
Qt程序打包成单个exe文件
文章目录 0. 准备工作1. 使用 windeployqt 提取必要的动态链接库和资源文件1.1 操作步骤1.2 补充 2. 使用 Enigma Virtual Box将文件夹打包成单个exe2.1 操作步骤 0. 准备工作 Qt程序打包用到的工具有: windeployqt :安装Qt时自带Enigma Virtual Box 下…...
【机器学习】GANs网络在图像和视频技术中的应用前景
📝个人主页:哈__ 期待您的关注 目录 1. 🔥引言 背景介绍 研究意义 2. 🎈GANs的基本概念和工作原理 生成对抗网络简介 工作原理 3. 🤖GANs在图像生成中的应用 图像超分辨率 工作原理 图像去噪 工作原理 图…...
MFC 使用sapi文字转换为语音
文章目录 添加头文件声明变量 添加头文件 声明变量 pSpVoice NULL; //默认构造函数中初始化为空 bool CChKBarSCCodeApp::InitSpVoice() {HRESULT hr ::CoInitialize(NULL); // COM初始化if (!SUCCEEDED(hr)){AfxMessageBox(_T("声音环境初始化失败!…...
(Git)多人协作1
文章目录 前言总结 前言 目标:master分支下file.txt文件新增“aaa”,“bbb” 实现:开发者1新增“aaa”,开发者2新增“bbb” 条件:在同一个分支下协作完成 实际开发过程中,每个用户都与属于自己的码云账户,如果想要进…...
MySQL-分组函数
041-分组函数 重点:所有的分组函数都是自动忽略NULL的 分组函数的执行原则:先分组,然后对每一组数据执行分组函数。如果没有分组语句group by的话,整张表的数据自成一组。 分组函数包括五个: max:最大值mi…...
【C语言】联合(共用体)
目录 一、什么是联合体 二、联合类型的声明 三、联合变量的创建 四、联合的特点 五、联合体大小的计算 六、联合的应用(判断大小端) 七、联合体的优缺点 7.1 优点 7.2 缺点 一、什么是联合体 联合也是一种特殊的自定义类型。由多个不同类型的数…...
【博客715】如何从victorimametrics集群中下线vmstorage节点
How to Decommission a vmstorage Node from a VictoriaMetrics Cluster 我们需要从VictoriaMetrics 集群中优雅地移除一个 vmstorage 节点。每个 vmstorage 节点都包含自己的数据部分,从集群中移除 vmstorage 节点会导致图表出现空白(因为复制超出了范…...
Redis缓存技术详解与实战
Redis缓存技术详解与实战 Redis作为一个开源的内存数据结构存储系统,它可以用作数据库、缓存和消息代理。在现代高并发、大数据量处理的系统中,Redis作为缓存层的应用越来越广泛。本文将详细讲解Redis在查询、添加缓存、更新缓存、缓存预热、缓存穿透、…...
业务架构的位置及关系
背景 我们已经了解了业务架构的核心元素组成,以及各个扩展元素,同时对各个元素的关系协同也有了一些了解,那么接下来,我们进一步在宏观层面来看业务架构与其他架构的关系。 企业架构 企业架构有多种理解,也有多种叫…...
CMS与AI的融合:构建万能表单小程序系统
引言: 随着人工智能技术的飞速发展,MyCMS作为一款功能强大的内容管理系统,通过集成AI技术,进一步拓展了其应用范围和智能化水平。本文将探讨如何利用MyCMS结合AI技术,构建一个能够将用户提交的万能表单数据转化为智能提…...
机器学习常见知识点 2:决策树
文章目录 决策树算法1、决策树树状图2、选择最优决策条件3、决策树算法过程→白话决策树原理决策树构建的基本步骤常见的决策树算法决策树的优缺点 【五分钟机器学习】可视化的决策过程:决策树 Decision Tree 关键词记忆: 纯度、选择最优特征分裂、熵、基…...
海洋CMS admin_notify.php 远程代码执行漏洞复现(CVE-2024-30565)
0x01 产品简介 海洋CMS是一套专为不同需求的站长而设计的内容管理系统,灵活、方便、人性化设计、简单易用是最大的特色,可快速建立一个海量内容的专业网站。海洋CMS基于PHP+MySql技术开发,完全开源免费 、无任何加密代码。 0x02 漏洞概述 海洋CMS admin_notify.php 接口处…...
Spring、Spring MVC、MyBatis和Spring Boot对比
在对比Spring、Spring MVC、MyBatis和Spring Boot时,我们可以从以下几个方面进行详细的分析: Spring框架: 作用:Spring是一个轻量级的IoC(控制反转)和AOP(面向切面编程)容器&#…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
自然语言处理——循环神经网络
自然语言处理——循环神经网络 循环神经网络应用到基于机器学习的自然语言处理任务序列到类别同步的序列到序列模式异步的序列到序列模式 参数学习和长程依赖问题基于门控的循环神经网络门控循环单元(GRU)长短期记忆神经网络(LSTM)…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
3-11单元格区域边界定位(End属性)学习笔记
返回一个Range 对象,只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意:它移动的位置必须是相连的有内容的单元格…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
Scrapy-Redis分布式爬虫架构的可扩展性与容错性增强:基于微服务与容器化的解决方案
在大数据时代,海量数据的采集与处理成为企业和研究机构获取信息的关键环节。Scrapy-Redis作为一种经典的分布式爬虫架构,在处理大规模数据抓取任务时展现出强大的能力。然而,随着业务规模的不断扩大和数据抓取需求的日益复杂,传统…...
springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...
