go context详解

Context

在Go语言圈子中流行着一句话:

Never start a goroutine without knowing how it will stop。

翻译:如果你不知道协程如何退出,就不要使用它。

在创建协程时,我们可能还会再创建一些别的子协程,那么这些协程的退出就成了问题。在Go1.7之后,Go官方引入了Context来实现协程的退出。不仅如此,Context还提供了跨协程、甚至是跨服务的退出管理。

Context本身的含义是上下文,我们可以理解为它内部携带了超时信息、退出信号,以及其他一些上下文相关的值(例如携带本次请求中上下游的唯一标识trace_id)。由于Context携带了上下文信息,父子协程之间就可以”联动“ 了。

Context标准库

在Context标准库中重要的结构 context.Context其实是一个接口,它提供了Deadline、Done、Err、Value这4种方法:

type Context interface {Deadline() (deadline time.Time, ok bool)Done() <-chan struct{}Err() errorValue(key interface{}) interface{}}

Deadline方法用于返回Context的过期时间。Deadline第一个返回值表示Context的过期时间,第二个返回值表示是否设置了过期时间,如果多次调用Deadline方法会返回相同的值。

Done是使用最频繁的方法,它会返回一个通道。一般的做法是调用者在select中监听该通道的信号,如果该通道关闭则表示服务超时或异常,需要执行后续退出逻辑。多次调用Done方法会返回相同的通道。

通道关闭后,Err方法回返回退出的原因。

Value方法返回指定Key对应的value,这是Context携带的值。Key必须是可比较的,一般用法Key是一个全局变量,通过context.WithValue将key存储到Context中,并通过Context.Value方法退出。

Context是一个接口,这意味着需要有对应的具体实现。用户可以自己实现Context接口,并严格遵守Context接口。

func (*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 interface{}) interface{} {return nil
}

因此,要具体使用Context,需要派生出新的Context。我们使用的最多的还是Go标准库中的实现。
前三个函数都用于派生出有退出功能的Context。

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context

WithCancel函数回返回一个子Context和cancel方法。子Context会在两种情况下触发退出:一种情况是调用者主动调用了返回的cancel方法;另一种情况是当参数中的父Context退出时,子Context将级联退出。
WithTimeout函数指定超时时间。当超时发生后,子Context将退出。因此,子Context的退出有三种时机,一种是父Context退出;一种是超时退出;最后一种是主动调用cancel函数退出。
WithDeadline和WithTimeout函数的处理方法相似,不过它们的参数指定的是最后到期的时间。
WithValue函数会返回带key-value的子Context。

Context实践

eg:

下面的代码中childCtx是preCtx的子Context,其设置的超时时间为300ms。但是preCtx的超时时间为100ms,因此父Context退出后,子Context会立即退出,实际的等待时间只有100ms。

func main() {ctx := context.Background()before := time.Now()preCtx, _ := context.WithTimeout(ctx, 100*time.Millisecond)go func() {childCtx, _ := context.WithTimeout(preCtx, 300*time.Millisecond)select {case <-childCtx.Done():after := time.Now()fmt.Println("child during:", after.Sub(before).Milliseconds())}}()select {case <-preCtx.Done():after := time.Now()fmt.Println("pre during:", after.Sub(before).Milliseconds())}}

这是输出如下,父Context与子Context退出的时间差接近100ms:

pre during: 104
child during: 104

当我们把preCtx的超时时间修改为500ms时:

preCtx ,_:= context.WithTimeout(ctx,500*time.Millisecond)

从新的输出中可以看出,子协程的退出不会影响父协程的退出。

child during: 304
pre during: 500

Context底层原理

Context在很大程度上利用了通道的一个特性:通道在close时,会通知所有监听它的协程。

每个派生出的子Context都会创建一个新的退出通道,这样,只要组织好Context之间的关系,就可以实现继承链上退出信号的传递。如图所示的三个协程中,关闭通道A会连带关闭调用链上的通道B,通道B会关闭通道C。

要使用context的退出功能,需要调用WithCancel或WithTimeout,派生出一个新的结构Context。WithCancel底层对应的结构为cancelCtx,WithTimeout底层对应的结构为timerCtx,timerCtx包装了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 callcause    error                 // set to non-nil by the first cancel call
}
type timerCtx struct {cancelCtxtimer *time.Timer // Under cancelCtx.mu.deadline time.Time
}

cancelCtx第一个字段保留了父Context的信息。children字段则保存了当前Context派生的子Context的信息,每个Context都会有一个单独的done通道。

而WithDeadline函数会先判断父Context设置的超时时间是否比当前Context的超时时间短,如果是,那么子协程会随着父Context的退出而退出,没有必要再设置定时器。

当我们使用了标准库中默认的Context实现时,propagateCancel函数将子Context加入父协程的children哈希表中,并开启一个定时器。当定时器到期时,会调用cancel方法关闭通道,级联关闭当前Context派生的子Context,并取消与父Context的绑定关系。这种特性就产生了调用链上连锁的退出反应。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/8000.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一篇迟来的未来展望的博客

各位大佬好 &#xff0c;这里是阿川的博客 &#xff0c; 祝您变得更强 个人主页&#xff1a;在线OJ的阿川 大佬的支持和鼓励&#xff0c;将是我成长路上最大的动力 阿川水平有限&#xff0c;如有错误&#xff0c;欢迎大佬指正 老师布置的任务&#xff0c;叫写一篇博客&…

04.2.配置应用集

配置应用集 应用集的意思就是&#xff1a;将多个监控项添加到一个应用集里面便于管理。 创建应用集 填写名称并添加 在监控项里面找到对应的自定义监控项更新到应用集里面 选择对应的监控项于应用集

【Sql-02】 求每个省份最新登陆的三条数据

SQL 输出要求数据准备sql查询结果 输出要求 要求输出&#xff0c;userid_1,logtime_1,userid_2,logtime_2,userid_3,logtime_3 数据准备 CREATE TABLE sqltest (province varchar(32) NOT NULL,userid varchar(250) DEFAULT NULL,logtime datetime ) ENGINEInnoDB DEFAULT C…

【Halcon 内存泄漏记录 - C#】

Halcon 内存泄漏记录 - C# 1. Bitmap 转 HImage2. new 之后要Dispose() 1. Bitmap 转 HImage 由于Bitmap 在转化时使用BitmapData数据&#xff0c;BitmapData数据无法Dispose, 所以在使用时&#xff0c;使用using&#xff1a; try{using (Bitmap b bmp.Clone() as Bitmap){if…

如何在 CentOS 7 上为 Nginx 创建自签名 SSL 证书

简介 TLS&#xff0c;即传输层安全性&#xff0c;及其前身SSL&#xff0c;即安全套接字层&#xff0c;是用于将普通流量包装在受保护的加密包中的网络协议。 使用这项技术&#xff0c;服务器可以在服务器和客户端之间安全地发送流量&#xff0c;而不会被外部方拦截。证书系统…

深度学习--生成对抗网络GAN

GAN简介 让我们先来简单了解一下GAN GAN的全称是Generative Adversarial Networks&#xff0c;中文称为“生成对抗网络”&#xff0c;是一种在深度学习领域广泛使用的无监督学习方法。 GAN主要由两部分组成&#xff1a;生成器和判别器。生成器的目标是尽可能地生成真实的样本数…

如何创建微信小程序?只需3步完成小程序制作

微信&#xff0c;中国最大的社交媒体应用程序&#xff0c;几个月前推出了微信小程序&#xff0c;这一神奇的功能立即大受欢迎。这些小程序让在中国注册的商业实体所有者创建一个小程序来与微信用户互动。这些小程序不需要在用户手机上进行任何安装&#xff0c;只需通过微信应用…

synchronized工作原理及最佳实践指南

1. synchronized的基本用法与案例分析 1.1. 同步实例方法&#xff1a;对象锁的基本概念 1.1.1. 代码示例&#xff1a;无同步情况下的问题 在没有同步机制的环境下&#xff0c;当多个线程访问同一对象的非同步方法时&#xff0c;会导致资源共享的问题&#xff0c;从而出现数据…

STM32微秒级别延时--F407--TIM1

基本配置&#xff1a; TIM1挂载在APB2总线上&#xff0c;150MHz经过15分频&#xff0c;得到10MHz计数频率&#xff0c;由于disable了自动重装载&#xff0c;所以只需要看下一次计数值是多少即可。 void TIM1_Delay_us(uint16_t us) //使用阻塞方式进行延时&#xff0c;ARR值不…

一次讲透 CSS 背景样式

文章导读&#xff1a;AI 辅助学习前端&#xff0c;包含入门、进阶、高级部分前端系列内容&#xff0c;当前是 CSS 的部分&#xff0c;瑶琴会持续更新&#xff0c;适合零基础的朋友&#xff0c;已有前端工作经验的可以不看&#xff0c;也可以当作基础知识回顾。 CSS 的背景样式…

算法训练营第61天|LeetCode 739. 每日温度 496.下一个更大元素 I

LeetCode 739. 每日温度 题目链接&#xff1a; LeetCode 739. 每日温度 代码&#xff1a; class Solution { public:vector<int> dailyTemperatures(vector<int>& temperatures) {stack<int>st;int size temperatures.size();vector<int>resu…

解决the --read-only option so it cannot execute this statement

程序报错如下&#xff1a; Caused by: org.springframework.jdbc.UncategorizedSQLException: StatementCallback; uncategorized SQLException for SQL [DELETE FROM S_P_USER_Y WHERE Y 2024 ]; SQL state [HY000]; error code [1290]; The MySQL server is running wit…

day23-等差数列划分

问题描述&#xff1a; 如果一个数列 至少有三个元素 &#xff0c;并且任意两个相邻元素之差相同&#xff0c;则称该数列为等差数列。 例如&#xff0c;[1,3,5,7,9]、[7,7,7,7] 和 [3,-1,-5,-9] 都是等差数列。 给你一个整数数组 nums &#xff0c;返回数组 nums 中所有为等差…

【C 数据结构-动态内存管理】4. 无用单元收集(垃圾回收机制)

文章目录 【 1. 问题描述与解决方法 】【 2. 中断回收机制 】 【 1. 问题描述与解决方法 】 问题描述 动态存储管理的运行机制可以概括为&#xff1a;当用户发出申请空间的请求后&#xff0c;系统向用户分配内存&#xff1b;用户运行结束释放存储空间后&#xff0c;系统回收内…

Fizzler库+C#:从微博抓取热点的最简单方法

概述 在这篇技术文章中&#xff0c;我们将深入研究如何利用Fizzler库结合C#语言&#xff0c;以实现从微博平台抓取热点信息的功能。微博作为中国乃至全球范围内具有重要影响力的社交媒体平台之一&#xff0c;在互联网信息传播中扮演着举足轻重的角色。通过Fizzler这一强大的.N…

Pytorch 实现情感分析

情感分析 情感分析是 NLP 一种应用场景&#xff0c;模型判断输入语句是积极的还是消极的&#xff0c;实际应用适用于评论、客服等多场景。情感分析通过 transformer 架构中的 encoder 层再加上情感分类层进行实现。 安装依赖 需要安装 Poytorch NLP 相关依赖 pip install t…

JVM学习笔记【基础篇:垃圾回收】

自动垃圾回收 C/C的内存管理 ⚫ 在C/C这类没有自动垃圾回收机制的语言中&#xff0c;一个对象如果不再使用&#xff0c;需要手动释放&#xff0c;否则就会出现 内存泄漏。我们称这种释放对象的过程为垃圾回收&#xff0c;而需要程序员编写代码进行回收的方式为手动回收。 ⚫ …

RTT潘多拉开发板上实现电源管理

简介 随着物联网(IoT)的兴起&#xff0c;产品对功耗的需求越来越强烈。作为数据采集的传感器节点通常需要在电池供电时长期工作&#xff0c;而作为联网的SOC也需要有快速的响应功能和较低的功耗。 在产品开发的起始阶段&#xff0c;首先考虑是尽快完成产品的功能开发。在产品…

数仓开发中期:理论巩固

一、数仓以及商业智能&#xff08;Data Warehousing and Business Intelligence, DW/BI&#xff09;系统 1.1数据操作和数据获取的区别 对所有组织来说&#xff0c;信息都是其最重要的财富之一。信息几乎总是用作两个目的:操作型记录的保存和分析型决策的制定。简单来说&…

Stack数据结构设计模板

第三章 栈、队列、数组 1.栈 1.1 顺序栈 #define MaxSize 20 typedef int ElemType; //顺序栈的定义 typedef struct {ElemType data[MaxSize];int top; }SqStack; // 初始化顺序栈 void InitSqStack(SqStack &S){S.top -1; }; // 入栈(增) bool Push(SqStack &S,El…