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(面向切面编程)容器&#…...
【Linux高级IO】select、poll、epoll
【Linux高级IO】select、poll、epoll toc 作者:爱写代码的刚子 时间:2024.6.5 前言:本篇博客将会介绍面试重点考察的select、poll、epoll IO: input && Output read && write 应用层read&&write的时候,…...
Etcd Raft架构设计和源码剖析2:数据流
Etcd Raft架构设计和源码剖析2:数据流 | Go语言充电站 前言 之前看到一幅描述etcd raft的流程图,感觉非常直观,但和自己看源码的又有些不同,所以自己模仿着画了一下,再介绍一下。 下图从左到右依次分为4个部分&…...
深入理解Qt多线程编程(QThreadPool)
多线程编程在现代软件开发中变得越来越重要,它能够提高应用程序的响应速度和处理性能。在Qt框架中,QThreadPool作为线程池管理工具,被频繁的使用。 目录 概述 接口介绍 底层原理解析 使用方法 概述 QThreadPool是Qt提供的一个线程池实现&a…...
Prisma数据库ORM框架学习
初始化项目 中文网站 点击快速开始,点击创建sql项目,后面一步一步往后走 这个博主也挺全的,推荐下 可以看这个页面初始化项目跟我下面是一样的,这里用得是ts,我下面是js,不需要额外的配置了 1.vscode打开一个空文件夹 2.npm init -y 初始化package.json 3.安装相关依赖 …...
Flutter-使用MethodChannel 实现与iOS交互
前言 使用 MethodChannel 在 Flutter 与原生 Android 和 iOS 之间进行通信,可以让你在 Flutter 应用中调用设备的原生功能。 基础概念 MethodChannel:Flutter 提供的通信机制,允许消息以方法调用的形式在 Flutter 与原生代码之间传递。方法…...
【星海随笔】云解决方案学习日志篇(一) ELK,kibana,Logstash安装
心路历程 本来想最近再研究研究DPDK的。但是自己做一个东西很多时候没有回报。因为自己的低学历问题,类似工作的面试都没有。所以很多东西学了很快就忘了,没有地方可以用。 今天看到了一个大佬,除了发型外,很多想法还是很共鸣的。 Shay Banon 决定开始跟…...
【leetcode】hot100 哈希表
1. 两数之和 1.1 题目 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。…...
每日5题Day22 - LeetCode 106 - 110
每一步向前都是向自己的梦想更近一步,坚持不懈,勇往直前! 第一题:106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode) class Solution {public TreeNode buildTree(int[] inorder, int[] postorder) {…...
【Python】读取文件夹中所有excel文件拼接成一个excel表格 的方法
我们平常会遇到下载了一些Excel文件放在一个文件夹下,而这些Excel文件的格式都一样,这时候需要批量这些文件合并成一个excel 文件里。 在Python中,我们可以使用pandas库来读取文件夹中的所有Excel文件,并将它们拼接成一个Excel表…...
7. 通配符和正则表达式
文章目录 7.1 通配符7.1.1 通配符介绍7.1.2 通配符示例 7.2 正则表达式7.2.1 grep命令7.2.2 基本正则表达式7.2.3 扩展正则表达式 7.1 通配符 在 Shell 中通配符用于查找文件名和目录名。它是由 Shell 处理的,只会出现在命令的参数中。 7.1.1 通配符介绍 * 匹…...
qq营销/东莞网络优化公司
一大早,U-Mail客服张小姐就被电话催得迅速进入工作状态,原来一位客户心急火燎的说邮件往外界投递失败,但不知什么原因?张小姐迅速联系到客户专享技术工程师刘工,迅速对客户进行远程诊断,即刻排除了故障&…...
平江高端网站建设/西安seo推广公司
1、react中文网 2、ECMAScript 6 入门-阮一峰 3、从零学习 React 技术栈系列教程 4、react在线编辑器-codepen 5、react在线编辑器-codepan 6、react native中文文档 基础语法部分 jsx语法 Eslint 代码规范 react全家桶系列 Redux 数据管理库 React-router 路由 I…...
wordpress首页非常慢/互联网推广软件
本文从产品和架构演进、性能及稳定性挑战与优化实践、超级APP运维体系、架构上的容灾规划四个方面分享了支付宝APP亿级用户的性能稳定性优化及运维实践。性能方面,主要介绍了性能、电量、流量、内存、存储五个方面的优化。稳定性方面,主要介绍了Crash优化…...
做瞹瞹嗳视频网站在线观看/网络营销招聘岗位有哪些
夜光序言: 即使身边满是凄荒、冷淡与麻木,风也会吹得柔和,心也能寻得一份温存 正文:接下来~~,实战项目:一个拥有答题页面的微信小程序 上图为实际效果图 那么如何制作出来呢~ 点击“开始测试”按钮&#x…...
去成都旅游攻略及费用/太原网站建设方案优化
【Updated by yepeng 2013-9-4: 部分调研结果分析】近期,IT168发起了一个2013年SOC安全管理平台应用状况调查,欢迎大家前往参与。导语; 近几年来,随着企业安全产品部署的越来越多,从而导致了企业对于IT人…...
群晖wordpress外网访问/外链代发2分一条
1,JDK下载jdk将jdk解压后放在/usr/local路径下/usr/local/jdk1.7.0_67做个软连接ln -s /usr/local/jdk1.7.0_67/ /usr/local/jdk修改/etc/profile文件添加以下内容######################################export JAVA_HOME/usr/local/jdk1.7.0_67export PATH$JAVA…...