网站的管理和维护/全国教育培训机构平台
文章目录
- Context
- 为什么需要Context
- 多任务超时例子
- Context结构
- Context各种使用方法
- 创建context
- valueCtx
- valueCtx结构体
- WithValue
- cancelCtx
- cancelCtx结构体
- withCancel
- timerCtx
- WithDeadline
- WithTimeout
- 总结
Context
为什么需要Context
Go语言需要Context主要是为了在并发环境中有效地管理请求的上下文信息。Context提供了在函数之间传递取消信号、超时、截止时间等元数据的一种标准方式。
原因
- 取消操作: 在并发环境中,当一个请求被取消或者超时时,需要有效地通知相关的协程停止正在进行的工作。使用Context可以通过传递取消信号来实现这一点。
- 超时控制: 在一些场景下,限制操作执行的时间是很重要的。Context提供了一个统一的方式来处理超时,确保在规定的时间内完成操作,防止程序无限期地等待。
- 传递上下文信息: Context可以用于传递请求的元数据,例如请求的ID、用户信息等。这在跨多个函数调用的情况下非常有用,避免了在函数参数中传递大量的上下文信息。
- 协程之间的通信: Go语言中的协程(goroutine)是轻量级的线程,它们之间需要有效地通信。Context提供了一个标准的方式来传递信号和元数据,以便协程之间协同工作。
- 资源管理: 在一些场景下,需要确保在函数执行完毕后释放相关的资源,不管函数是正常执行还是因为取消或超时而提前退出。Context可以帮助在正确的时机释放资源。
- 综上所述,Context是Go语言中处理并发、超时和取消等问题的一种优雅而一致的方式,使得代码更加健壮、可维护,并且更容易在不同的并发场景中工作。
多任务超时例子
我们都知道在go语言并发编程中,我们可以采用select来监听协程的的通道控制协程,但是如下面的这种情况仅仅凭借select就显得有些无能为力:
- 支持多级嵌套,父任务停止后,子任务自动停止
- 控制停止顺序,先停EFG 再停BCD 最后停A
目标1还好说,目标2好像就没那么灵活了,正式讨论context如何解决这些问题前,我们先看下常规context的使用
Context结构
context 包是 Go 语言中用于处理请求的上下文的标准库之一。它提供了一种在函数之间传递取消信号、超时和截止时间的机制。
type Context interface {Deadline() (deadline time.Time, ok bool)Done() <-chan struct{}Err() errorValue(key interface{}) interface{}
}
-
Deadline()
返回一个完成工作的截止时间,表示上下文应该被取消的时间。如果 ok==false 表示没有设置截止时间。 -
Done()
返回一个 Channel,这个 Channel 会在当前工作完成时被关闭,表示上下文应该被取消。如果无法取消此上下文,则 Done 可能返回 nil。多次调用 Done 方法会返回同一个 Channel。 -
Err()
返回 Context 结束的原因,它只会在 Done 方法对应的 Channel 关闭时返回非空值。如果 Context 被取消,会返回context.Canceled 错误;如果 Context 超时,会返回context.DeadlineExceeded错误。 -
Value()
从 Context 中获取键对应的值。如果未设置 key 对应的值则返回 nil。以相同 key 多次调用会返回相同的结果。
另外,context 包中提供了两个创建默认上下文的函数:
// TODO 返回一个非 nil 但空的上下文。
// 当不清楚要使用哪种上下文或无可用上下文尚应使用 context.TODO。
func TODO() Context// Background 返回一个非 nil 但空的上下文。
// 它不会被 cancel,没有值,也没有截止时间。它通常由 main 函数、初始化和测试使用,并作为处理请求的顶级上下文。
func Background() Context
还有四个基于父级创建不同类型上下文的函数:
// WithCancel 基于父级创建一个具有 Done channel 的 context
func WithCancel(parent Context) (Context, CancelFunc)// WithDeadline 基于父级创建一个不晚于 d 结束的 context
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)// WithTimeout 等同于 WithDeadline(parent, time.Now().Add(timeout))
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)// WithValue 基于父级创建一个包含指定 key 和 value 的 context
func WithValue(parent Context, key, val interface{}) Context
在后面会详细介绍这些不同类型 context 的用法。
Context各种使用方法
创建context
context包主要提供了两种方式创建context:
- context.Backgroud()
- context.TODO()
这两个函数其实只是互为别名,没有差别,官方给的定义是:
- context.Background 是上下文的默认值,所有其他的上下文都应该从它衍生(Derived)出来。
- context.TODO 应该只在不确定应该使用哪种上下文时使用;
所以在大多数情况下,我们都使用context.Background作为起始的上下文向下传递。
上面的两种方式是创建根context,不具备任何功能,具体实践还是要依靠context包提供的With系列函数来进行派生:
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context
valueCtx
valueCtx结构体
type valueCtx struct {Contextkey, val interface{}
}func (c *valueCtx) Value(key interface{}) interface{} {if c.key == key {return c.val}return c.Context.Value(key)
}
valueCtx利用一个Context类型的变量来表示父节点context,所以当前context继承了父context的所有信息;valueCtx类型还携带一组键值对,也就是说这种context可以携带额外的信息。valueCtx实现了Value方法,用以在context链路上获取key对应的值,如果当前context上不存在需要的key,会沿着context链向上寻找key对应的值,直到根节点。
WithValue
我们日常在业务开发中都希望能有一个trace_id能串联所有的日志,这就需要我们打印日志时能够获取到这个trace_id,在python中我们可以用gevent.local来传递,在java中我们可以用ThreadLocal来传递,在Go语言中我们就可以使用Context来传递,通过使用WithValue来创建一个携带trace_id的context,然后不断透传下去,打印日志时输出即可,来看使用例子:
package mainimport ("context""fmt" // 我们需要使用fmt包中的Println()函数"strings""time""github.com/google/uuid"
)const (KEY = "trace_id"
)// 生成随机ID
func NewRequestID() string {return strings.Replace(uuid.New().String(), "-", "", -1)
}
// 生成携带值的context
func NewContextWithTraceID() context.Context {ctx := context.WithValue(context.Background(), KEY, NewRequestID())return ctx
}
//打印数据
func PrintLog(ctx context.Context, message string) {fmt.Printf("%s|info|trace_id=%s|%s", time.Now().Format("2006-01-02 15:04:05"), GetContextValue(ctx, KEY), message)
}
// 获取context中的值
func GetContextValue(ctx context.Context, k string) string {v, ok := ctx.Value(k).(string)if !ok {return ""}return v
}func ProcessEnter(ctx context.Context) {PrintLog(ctx, "Golang梦工厂")
}func main() {ProcessEnter(NewContextWithTraceID())
}
结果
2024-01-10 18:55:03|info|trace_id=c4eeb76d427449fda52a4775ccbc0509|Golang梦工厂
cancelCtx
cancelCtx结构体
type cancelCtx struct {Contextmu sync.Mutex // 同步锁,保护下面的所有字段done chan struct{} //惰性创建,由第一次取消调用关闭children map[canceler]struct{} // 在第一次取消调用时,设置为 nilerr error // 在第一次取消调用时设置为 non-nil
}type canceler interface {cancel(removeFromParent bool, err error)Done() <-chan struct{}
}
跟valueCtx类似,cancelCtx中也有一个context变量作为父节点;变量done表示一个channel,用来表示传递关闭信号;children表示一个map,存储了当前context节点下的子节点;err用于存储错误信息表示任务结束的原因。
withCancel
日常业务开发中我们往往为了完成一个复杂的需求会开多个gouroutine去做一些事情,这就导致我们会在一次请求中开了多个goroutine确无法控制他们,这时我们就可以使用withCancel来衍生一个context传递到不同的goroutine中,当我想让这些goroutine停止运行,就可以调用cancel来进行取消。
package mainimport ("context""fmt""time"
)func main() {ctx, cancel := context.WithCancel(context.Background())go Speak(ctx)time.Sleep(10 * time.Second)cancel()time.Sleep(1 * time.Second)
}func Speak(ctx context.Context) {for range time.Tick(time.Second) {select {case <-ctx.Done():fmt.Println("我要闭嘴了")returndefault:fmt.Println("balabalabalabala")}}
}
timerCtx
timerCtx是一种基于cancelCtx的context类型,从字面上就能看出,这是一种可以定时取消的context。
type timerCtx struct {cancelCtxtimer *time.Timer // Under cancelCtx.mu.deadline time.Time
}func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {return c.deadline, true
}func (c *timerCtx) cancel(removeFromParent bool, err error) {//将内部的cancelCtx取消c.cancelCtx.cancel(false, err)if removeFromParent {// Remove this timerCtx from its parent cancelCtx's children.removeChild(c.cancelCtx.Context, c)}c.mu.Lock()if c.timer != nil {取消计时器c.timer.Stop()c.timer = nil}c.mu.Unlock()
}
timerCtx内部使用cancelCtx实现取消,另外使用定时器timer和过期时间deadline实现定时取消的功能。timerCtx在调用cancel方法,会先将内部的cancelCtx取消,如果需要则将自己从cancelCtx祖先节点上移除,最后取消计时器。
WithDeadline
WithDeadline 用于设置一个绝对时间,表示在某个具体的时间点超时,例如 context.WithDeadline(parentContext, time.Now().Add(10 * time.Second)) 表示在当前时间的 10 秒后超时。
package mainimport ("context""fmt""time"
)func main() {HttpHandler()
}func NewContextWithTimeout() (context.Context, context.CancelFunc) {return context.WithDeadline(context.Background(), time.Now().Add(10*time.Second))
}func HttpHandler() {ctx, cancel := NewContextWithTimeout()defer cancel()deal(ctx)
}func deal(ctx context.Context) {for i := 0; i < 10; i++ {time.Sleep(1 * time.Second)select {case <-ctx.Done():fmt.Println(ctx.Err())returndefault:fmt.Printf("deal time is %d\n", i)}}
}
WithTimeout
WithTimeout 用于设置一个相对时间,表示在多长时间后超时,例如 context.WithTimeout(parentContext, 5 * time.Second) 表示在 5 秒后超时。
package mainimport ("context""fmt""time"
)func main() {HttpHandler()
}func NewContextWithTimeout() (context.Context, context.CancelFunc) {return context.WithTimeout(context.Background(), 3*time.Second)
}func HttpHandler() {ctx, cancel := NewContextWithTimeout()defer cancel()deal(ctx)
}func deal(ctx context.Context) {for i := 0; i < 10; i++ {time.Sleep(1 * time.Second)select {case <-ctx.Done():fmt.Println(ctx.Err())returndefault:fmt.Printf("deal time is %d\n", i)}}
}
总结
context主要用于父子任务之间的同步取消信号,本质上是一种协程调度的方式。另外在使用context时有两点值得注意:上游任务仅仅使用context通知下游任务不再需要,但不会直接干涉和中断下游任务的执行,由下游任务自行决定后续的处理操作,也就是说context的取消操作是无侵入的;context是线程安全的,因为context本身是不可变的(immutable),因此可以放心地在多个协程中传递使用。
相关文章:

GO语言Context的作用
文章目录 Context为什么需要Context多任务超时例子Context结构 Context各种使用方法创建contextvalueCtxvalueCtx结构体WithValue cancelCtxcancelCtx结构体withCancel timerCtxWithDeadlineWithTimeout 总结 Context 为什么需要Context Go语言需要Context主要是为了在并发环…...

金和OA C6 upload_json 任意文件上传漏洞
产品介绍 金和网络是专业信息化服务商,为城市监管部门提供了互联网监管解决方案,为企事业单位提供组织协同OA系统开发平台,电子政务一体化平台,智慧电商平台等服务。 漏洞概述 金和 OA C6 upload_json接口处存在任意文件上传漏洞,攻击者可以通过构造特殊请求包上…...

大模型学习第四课
学习目标: XTuner 大模型单卡低成本微调实战 学习内容: Finetune简介XTuner介绍8GB显卡玩转LLM动手实战环节 学习时间: 20240110 学习产出: Finetune简介 增量预训练微调指令跟随微调LoRA,QLoRAXTuner 简介:适配多…...

Code Runner使用外部控制台,运行结束后等待用户输入
问题描述 网上让程序运行结束暂停的方法大多数只有两种: 1.末尾加上system(“pause”) 2.start /k cmd 第一种方法每一个程序都需要在最后加上这条命令很烦; 第二章方法cmd窗口在程序运行结束后不会自动关闭,需要用户手动关闭 我想找到一种…...

IC设计的前端和后端是如何区分的?
一、工作着重点不同 **1、IC前端:**根据芯片规格书完成SOC的设计和集成, 使用仿真验证工具完成SOC的设计验证。 **2、IC后端:**将前端设计产生的门级网表通过EDA设计工具进行布局布线和进行物理验证并最终产生供制造用的GDSII数据 二、工作…...

Unity WebView 中文输入支持
使用版本:Vuplex 3D WebView for Windows v4.4; 测试环境:unity editor 2020.3.40f1c1、Windows; 1、打开脚本CanvasWebVie!wPrefab 2、找到_initCanvasPrefab方法,约略在459行附近 3、添加一行代码: …...

x-cmd pkg | trdsql - 能对 CSV、LTSV、JSON 和 TBLN 执行 SQL 查询的工具
目录 简介首次用户技术特点竞品和相关作品进一步阅读 简介 trdsql 是一个使用 sql 作为 DSL 的强大工具: 采用 SQL 对 CSV、LTSV、JSON 和 TBLN 文件执行查询与 MySQL,Postgresql,Sqlite 的 Driver 协同,可以实现对应数据库的表与文件的 JO…...

Camunda Spin
Spin 常用于在脚本中解析json或者xml使用,S(variable) 表示构造成Spin对象,通过prop(“属性名”)获取属性值,通过stringValue()、numberValue()、boolValue() 等对类型转换。 repositoryService.createDeployment().name("消息事件流程&…...

strlen/Memcpy_s/strncasecmp
strlen 声明:size_t strlen(const char *str) 举例: #include <stdio.h> #include <string.h>int main () {char str[50];int len;strcpy(str, "This is runoob.com");len strlen(str);printf("|%s| 的长度是 |%d|\n"…...

水经微图安卓版APP正式上线!
在水经微图APP(简称“微图APP”)安卓版已正式上线! 在随着IOS版上线约一周之后,安卓版终于紧随其后发布了。 微图安卓版APP下载安装 自从IOS版发布之后,就有用户一直在问安卓版什么时候发布,这里非常感谢…...

数据结构第十二弹---堆的应用
堆的应用 1、堆排序2、TopK问题3、堆的相关习题总结 1、堆排序 要学习堆排序,首先要学习堆的向下调整算法,因为要用堆排序,你首先得建堆,而建堆需要执行多次堆的向下调整算法。 但是,使用向下调整算法需要满足一个前提…...

[NSSRound#3 Team]This1sMysql
[NSSRound#3 Team]This1sMysql 源码 <?php show_source(__FILE__); include("class.php"); $conn new mysqli();if(isset($_POST[config]) && is_array($_POST[config])){foreach($_POST[config] as $key > $val){$value is_numeric($var)?(int)$…...

Android 通知简介
Android 通知简介 1. 基本通知 图1: 基本通知详情 小图标 : 必须提供,通过 setSmallIcon( ) 进行设置.应用名称 : 由系统提供.时间戳 : 由系统提供,也可隐藏时间.大图标(可选) : 可选内容(通常仅用于联系人照片,请勿将其用于应用图标),通过setLargeIcon( ) 进行设置.标题 : 可选…...

QT开发 2024最新版本优雅的使用vscode开发QT
▬▬▬▬▬▶VS开发QT◀▬▬▬▬▬ 🎄先看效果 🎄编辑环境变量 如图添加环境变量!!! 东西全在QT的安装目录!!! 找到的按照我的教程再装一次!!! 点…...

Redis性能大挑战:深入剖析缓存抖动现象及有效应对的战术指南
在实际应用中,你是否遇到过这样的情况,本来Redis运行的好好的,响应也挺正常,但突然就变慢了,响应时间增加了,这不仅会影响用户体验,还会牵连其他系统。 那如何排查Redis变慢的情况呢?…...

基于SpringBoot的教学管理系统
文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式 🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 &…...

机器学习之独热编码(One-Hot)
一、背景 在机器学习算法中,我们经常会遇到分类特征,例如:人的性别有男女,祖国有中国,美国,法国等。这些特征值并不是连续的,而是离散的,无序的。通常我们需要对其进行特征数字化。…...

IIS+SDK+VS2010+SP1+SQL server2012全套工具包及安装教程
前言 今天花了两个半小时安装这一整套配置,这个文章的目标是将安装时间缩短到1个小时 正文 安装步骤如下: VS2010 —> service pack 1 —>SQL server2012 —> IIS —> SDK 工具包链接如下: https://pan.baidu.com/s/1WQD-KfiUW…...

【昕宝爸爸小模块】HashMap用在并发场景存在的问题
HashMap用在并发场景存在的问题 一、✅典型解析1.1 ✅JDK 1.8中1.2 ✅JDK 1.7中1.3 ✅如何避免这些问题 二、 ✅HashMap并发场景详解2.1 ✅扩容过程2.2 ✅ 并发现象 三、✅拓展知识仓3.1 ✅1.7为什么要将rehash的节点作为新链表的根节点3.2 ✅1.8是如何解决这个问题的3.3 ✅除了…...

数据库索引
1、什么是索引?为什么要用索引? 1.1、索引的含义 数据库索引,是数据库管理系统中一个排序的数据结构,以协助快速查询,更新数据库中表的数据。索引的实现通常使用B树和变种的B树(MySQL常用的索引就是B树&…...

开源知识库工具推荐:低成本搭建知识库
在信息爆炸的时代,企业和个体对知识的存储和管理需求日益增强。开源知识库工具因其开源、免费、高效的特性,成为了众多组织和个人的首选。如果你正在寻找一款优秀的开源知识库工具,本文将为你推荐三款性能优异的产品,感兴趣就往下…...

C# Chart控件
// 定义图表区域 this.chart1.ChartAreas.Clear(); ChartArea chartArea1 new ChartArea("C1"); this.chart1.ChartAreas.Add(chartArea1); //定义存储和显示点的容器 this.chart1.Series.Clear(); Series series1 new Series("OK"); //series1.ChartAre…...

OpenCV C++ 图像处理实战 ——《多尺度自适应Gamma矫正的低照图像增强》
OpenCV C++ 图像处理实战 ——《多尺度自适应Gamma矫正的低照图像增强》 一、结果演示二、多尺度自适应Gamma矫正的低照度图像增强2.1HSI颜色空间2.1.1 功能源码2.2 自适应于直方图分布的 Gamma 矫正2.2.1 功能源码2.3 多尺度 Retinex 分解与明度增强2.3.1 功能源码三、源码测试…...

原型模式
为什么要使用原型模式 不用重新初始化对象,而是动态地获得对象运行时的状态。适用于当创建对象的成本较高时,如需进行复杂的数据库操作或复杂计算才能获得初始数据。 优点是可以隐藏对象创建的细节,减少重复的初始化代码;可以在…...

linux centos 账户管理命令
在CentOS或其他基于Linux的系统上,账户管理涉及到用户的创建、修改、删除以及密码的管理等任务。 linux Centos账户管理命令 1 创建用户: useradd username 这将创建一个新用户,但默认不会创建家目录。如果想要创建家目录,可以…...

【JavaWeb学习笔记】19 - 网购家居项目开发(上)
一、项目开发流程 程序框架图 项目具体分层方案 MVC 1、说明是MVC MVC全称: Mode模型、View视图、Controller控制器。 MVC最早出现在JavaEE三层中的Web层,它可以有效的指导WEB层的代码如何有效分离,单独工作。 View视图:只负责数据和界面的显示&…...

强化学习的数学原理学习笔记 - RL基础知识
文章目录 Roadmap🟡基础概念贝尔曼方程(Bellman Equation)基本形式矩阵-向量形式迭代求解状态值 vs. 动作值 🟡贝尔曼最优方程(Bellman Optimality Equation,BOE)基本形式迭代求解 本系列文章介…...

winSCP是什么?它有什么功能和特性?它值不值得我们去学习?我们该如何去学习呢?
WinSCP是一款免费的开源SFTP、SCP、FTP和WebDAV客户端,用于Windows操作系统。它提供了一个图形化界面,使用户可以方便地在本地计算机和远程计算机之间传输文件。 WinSCP支持SSH加密通信和多种认证方法,包括密码、公钥和键盘交互。它还支持自…...

SpringBoot的数据层解决方案
🙈作者简介:练习时长两年半的Java up主 🙉个人主页:程序员老茶 🙊 ps:点赞👍是免费的,却可以让写博客的作者开心好久好久😎 📚系列专栏:Java全栈,…...

极客时间-《如何成为学习高手》文章笔记 + 个人思考
极客时间-《如何成为学习高手》文章笔记 个人思考 底层思维高效学习05|教你全面提升专注力,学习时不再走神06|教你高效复习:巧用学习神器取得好成绩07|我考北大中文系时,15 天背下 10 门专业课的连点成线法…...