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

Golang 如何基于现有的 context 创建新的 context?

目录

基于现有的 context 创建新的 context

现有创建方法的问题

Go 1.21 中的 context.WithoutCancel 函数

Go 版本低于 1.21 该怎么办?


在 Golang 中,context 包提供了创建和管理上下文的功能。当需要基于现有的 context.Context 创建新的 context 时,通常是为了添加额外的控制信息或为了满足特定的生命周期需求。

基于现有的 context 创建新的 context

可以基于现有的 context.Context 创建一个新的 context,对应的函数有 context.WithCancel、context.WithDeadline、context.WithTimeout 或 context.WithValue。这些函数会返回一个新的 context.Context 实例,继承了原来 context 的行为,并添加了新的行为或值。使用 context.WithValue 函数创建的简单示例代码如下:

package mainimport "context"func main() {// 假设已经有了一个context ctxctx := context.Background()// 可以通过context.WithValue创建一个新的contextkey := "myKey"value := "myValue"newCtx := context.WithValue(ctx, key, value)// 现在newCtx包含了原始ctx的所有数据,加上新添加的键值对
}

使用 context.WithCancel 函数创建,简单示例代码如下:

package mainimport "context"func main() {// 假设已经有了一个context ctxctx := context.Background()// 创建一个可取消的contextnewCtx, cancel := context.WithCancel(ctx)// 当完成了newCtx的使用,可以调用cancel来取消它// 这将释放与该context相关的资源defer cancel()
}

现有创建方法的问题

先说一个使用场景:一个接口处理完基本的任务之后,后续一些处理的任务放使用新开的 Goroutine 来处理,这时候会基于当前的 context 创建一个 context(可以使用上面提到的方法来创建) 给 Goroutine 使用,也不需要控制 Goroutine 的超时时间。

这种场景下,Goroutine 的声明周期一般都会比这个接口的生命周期长,这就会出现一个问题——当前接口请求所属的 Goroutine 退出后会导致 context 被 cancel,进而导致新开的 Goroutine 中的 context 跟着被 cancel, 从而导致程序异常。看一个示例:

package mainimport ("bytes""context""errors""fmt""io""net/http""github.com/gin-gonic/gin"
)func main() {r := gin.New()r.GET("/test", func(c *gin.Context) {// 父 context,有使用取消功能ctx, cancel := context.WithCancel(c)defer cancel()// 创建子 context 给新开的 Goroutine 使用ctxCopy, _ := context.WithCancel(ctx)go func() {err := TestPost(ctxCopy)fmt.Println(err)}()})r.Run(":8080")
}func TestPost(ctx context.Context) error {fmt.Println("goroutine...")buffer := bytes.NewBuffer([]byte(`{"xxx":"xxx"}`))request, err := http.NewRequest("POST", "http://xxx.luduoxin.com/xxx", buffer)if err != nil {return err}request.Header.Set("Content-Type", "application/json")client := http.Client{}rsp, err := client.Do(request.WithContext(ctx))if err != nil {return err}defer func() {_ = rsp.Body.Close()}()if rsp.StatusCode != http.StatusOK {return errors.New("response exception")}_, err = io.ReadAll(rsp.Body)if err != nil {return err}return nil
}

运行代码,在浏览器中访问 http://127.0.0.1:8080/test,控制台会打印如下错误信息:

goroutine...
Post "http://xxx.luduoxin.com/xxx": context canceled

可以看出,因为父级 context 被 cancel,导致子 context 也被 cancel,从而导致程序异常。因此,需要一种既能继承父 context 所有的 value 信息,又能去除父级 context 的 cancel 机制的创建函数。

Go 1.21 中的 context.WithoutCancel 函数

这种函数该如何实现呢?其实 Golang 从 1.21 版本开始为我们提供了这样一个函数,就是 context 包中的 WithoutCancel 函数。源代码如下:

func WithoutCancel(parent Context) Context {if parent == nil {panic("cannot create context from nil parent")}return withoutCancelCtx{parent}
}type withoutCancelCtx struct {c Context
}func (withoutCancelCtx) Deadline() (deadline time.Time, ok bool) {return
}func (withoutCancelCtx) Done() <-chan struct{} {return nil
}func (withoutCancelCtx) Err() error {return nil
}func (c withoutCancelCtx) Value(key any) any {return value(c, key)
}func (c withoutCancelCtx) String() string {return contextName(c.c) + ".WithoutCancel"
}

原理其实很简单,主要功能是创建一个新的 context 类型,继承了父 context 的所有属性,但重写了 Deadline、Done、Err、Value 几个方法,当父 context 被取消时不会触发任何操作。

Go 版本低于 1.21 该怎么办?

如果 Go 版本低于 1.21 其实也很好办,按照 Go 1.21 中的实现方式自己实现一个就可以了,代码可以进一步精简,示例代码如下:

func WithoutCancel(parent Context) Context {if parent == nil {panic("cannot create context from nil parent")}return withoutCancelCtx{parent}
}type withoutCancelCtx struct {context.Context
}func (withoutCancelCtx) Deadline() (deadline time.Time, ok bool) {return
}func (withoutCancelCtx) Done() <-chan struct{} {return nil
}func (withoutCancelCtx) Err() error {return nil
}

使用自己实现的这个版本再跑一下之前的示例,代码如下:

package mainimport ("bytes""context""errors""fmt""io""net/http""time""github.com/gin-gonic/gin"
)func main() {r := gin.New()r.GET("/test", func(c *gin.Context) {// 父 context,有使用取消功能ctx, cancel := context.WithCancel(c)defer cancel()// 创建子 context 给新开的 Goroutine 使用ctxCopy := WithoutCancel(ctx)go func() {err := TestPost(ctxCopy)fmt.Println(err)}()})r.Run(":8080")
}func WithoutCancel(parent Context) Context {if parent == nil {panic("cannot create context from nil parent")}return withoutCancelCtx{parent}
}type withoutCancelCtx struct {context.Context
}func (withoutCancelCtx) Deadline() (deadline time.Time, ok bool) {return
}func (withoutCancelCtx) Done() <-chan struct{} {return nil
}func (withoutCancelCtx) Err() error {return nil
}func TestPost(ctx context.Context) error {fmt.Println("goroutine...")buffer := bytes.NewBuffer([]byte(`{"xxx":"xxx"}`))request, err := http.NewRequest("POST", "http://xxx.luduoxin.com/xxx", buffer)if err != nil {return err}request.Header.Set("Content-Type", "application/json")client := http.Client{}rsp, err := client.Do(request.WithContext(ctx))if err != nil {return err}defer func() {_ = rsp.Body.Close()}()if rsp.StatusCode != http.StatusOK {return errors.New("response exception")}_, err = io.ReadAll(rsp.Body)if err != nil {return err}return nil
}type Context interface {Deadline() (deadline time.Time, ok bool)Done() <-chan struct{}Err() errorValue(key any) any
}

运行代码,在浏览器中访问 http://127.0.0.1:8080/test,发现不再报父 context 被 cancel 导致的报错了。

相关文章:

Golang 如何基于现有的 context 创建新的 context?

目录 基于现有的 context 创建新的 context 现有创建方法的问题 Go 1.21 中的 context.WithoutCancel 函数 Go 版本低于 1.21 该怎么办&#xff1f; 在 Golang 中&#xff0c;context 包提供了创建和管理上下文的功能。当需要基于现有的 context.Context 创建新的 context …...

【学习笔记】[AGC063E] Child to Parent

提供一个多项式做法。 分别设 f u , i , g u , i f_{u,i},g_{u,i} fu,i​,gu,i​表示以 u u u为根时&#xff0c; a u i a_ui au​i和 a u ≥ i a_u\ge i au​≥i的方案数&#xff0c;合并子树 v v v时&#xff0c;转移如下&#xff1a; f u , i ∑ f u , i − k r g v . k…...

sar 运行出错

手机上使用sar 使用sar工具报错 / # sar -I SUM 1 1 Cannot find the data collector (sadc) exec: No such file or directory Inconsistent input data解决方法&#xff1a;需要将 sadc sadf sar 三个bin同时推到/usr/bin/目录下 / # sar -I SUM 1 2 Linux 5.15.104-ab558…...

UE5 C++的TCP服务器与客户端

客户端.h 需要在Build.cs中加入模块:"Networking","Sockets","Json","JsonUtilities" // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h" #include…...

nginx+lua配置,一个域名配置https,docker集群使用

没安装kua的先安装lua 没有resty.http模块的&#xff0c;许配置 nginxlua配置&#xff0c;一个域名配置https&#xff0c;docker集群使用&#xff0c;一个域名配置https管理整个集群 lua做转发&#xff08;方向代理&#xff09; 1、ad_load.lua文件 ngx.header.content_typ…...

jQuery 正则表达式 验证表单

文章目录 简介&#xff1a;什么是正则表达式以及作用&#xff1a;●文本框内容的验证&#xff1a;代码演示示例&#xff1a; 简介&#xff1a; jQuery Form插件是一个优秀的Ajax表单插件&#xff0c;可以非常容易地、无侵入地升级HTML表单以支持Ajax。jQuery Form有两个核心方法…...

如何使用SVN查看旧版本

和目录 第一步&#xff1a;打开SVN客户端 第二步&#xff1a;浏览历史版本 第三步&#xff1a;还原历史版本 结论 Subversion (缩写为SVN)是一种常用的版本控制系统&#xff0c;它可以帮助团队协作开发软件项目。除了基本的版本控制功能外&#xff0c;SVN还提供了许多其他功…...

使用 GitHub 远程仓库

使用 GitHub 远程仓库 GitHub 是最大的 Git 版本库托管商&#xff0c;是成千上万的开发者和项目能够合作进行的中心。 大部分 Git 版本库都托管在 GitHub&#xff0c;很多开源项目使用 GitHub 实现 Git 托管、问题追踪、代码审查以及其它事情。本篇文章主要带大家上手 GitHub …...

关键词提取

在自然语言处理领域中&#xff0c;处理海量文本信息的关键在于把用户关心的问题提取出来。而关键词是能够表达文档中心内容的词语&#xff0c;更是表达文档主题的最小单位。因此&#xff0c;文本关键词的提取对于文本信息的理解是至关重要的。 关键词提取是文本挖掘领域下的一个…...

web开发学习笔记(2.js)

1.引入 2.js的两种引入方式 3.输出语句 4.全等运算符 5.定义函数 6.数组 7.数组属性 8.字符串对象的对应方法 9.自定义对象 10.json对象 11.bom属性 12.window属性 13.定时刷新时间 14.跳转网址 15.DOM文档对象模型 16.获取DOM对象&#xff0c;根据DOM对象来操作网页 如下图…...

Vue Axios——前端技术栈

文章目录 基本介绍Vue是什么&#xff1f; MVVMVue的使用快速入门注意事项和使用细节 Vue 数据绑定机制分析数据单向渲染注意事项和细节 双向数据绑定事件绑定示例&#xff1a;注意事项和使用细节课后作业1课后作业2 修饰符示例 条件渲染/控制: v-if v-showv-if VS v-show课后作…...

九、Qt C++ 数据库开发

《一、QT的前世今生》 《二、QT下载、安装及问题解决(windows系统)》《三、Qt Creator使用》 ​​​ 《四、Qt 的第一个demo-CSDN博客》 《五、带登录窗体的demo》 《六、新建窗体时&#xff0c;几种窗体的区别》 《七、Qt 信号和槽》 《八、Qt C 毕业设计》 《九、Qt …...

力扣电话号码的组合

文章目录 题目说明做题思路代码实现代码解析 题目链接 题目说明 首先我们先分析一下这个题目题目中说呢先给出一个字符串这个字符串其实就是这个九键数字我们要按照要求将数字所代表的字符进行自由组合形成一个字符串并且这个字符串的长度和输入的数字字符串长度相同&#xff0…...

ZooKeeper 实战(五) Curator实现分布式锁

文章目录 ZooKeeper 实战(五) Curator实现分布式锁1.简介1.1.分布式锁概念1.2.Curator 分布式锁的实现方式1.3.分布式锁接口 2.准备工作3.分布式可重入锁3.1.锁对象3.2.非重入式抢占锁测试代码输出日志 3.3.重入式抢占锁测试代码输出日志 4.分布式非可重入锁4.1.锁对象4.2.重入…...

基于kubernetes部署MySQL主从环境

部署方式 通过部署mysql主从容器&#xff0c;配置主从pod之间数据同步。 配置数据库访问的密码 创建 Mysql 密码的 Secret [rootk8s-master1 master]# kubectl create secret generic mysql-password --namespaceapp --from-literalmysql_root_passwordroot secret/mysql-pas…...

【JAVA语言-第13话】异常处理 之 try-catch-finally,throws,throw关键字的详细解析

目录 异常处理 1.1 概述 1.2 异常分类 1.3 异常处理 1.3.1 throws 1.3.2 try-catch 1.3.3 finally代码块 1.3.4 throw关键字 1.3.5 throw和throws的区别 1.4 自定义异常 1.4.1 概述 1.4.2 定义 1.4.3 自定义异常练习 异常处理 1.1 概述 在Java中&#xff0c;异常…...

ChatGPT4.0 >ChatGPT 3.5 > 文心一言

文章目录 前言一、ChatGPT4.0与ChatGPT3.5相比具有以下优点&#xff1a;二、ChatGPT和文心一言相比具有以下优点&#xff1a;总结 前言 ChatGPT是一种基于自然语言处理的对话型人工智能模型&#xff0c;由OpenAI开发。它是使用了大规模的语料库进行无监督学习的结果&#xff0…...

Linux 入门命令大全汇总 + Linux 集锦大全 【20240115】

文章目录 Linux 入门命令大全汇总Linux 集锦大全更多信息 Linux 入门命令大全汇总 别有一番风趣的alias 刚刚好合适的 apropos 命令 迷你计算器 bc 可看黄道吉日的 cal 全文可查看&#xff1a; Linux入门命令大全全文 Linux 集锦大全 linux终端中最漂亮的几款字体介绍及…...

【Web】NSSCTF Round#16 Basic个人wp(全)

出题友好&#xff0c;适合手生复健。 目录 ①RCE但是没有完全RCE ②了解过PHP特性吗 ①RCE但是没有完全RCE 上来就是一段md5八股 (string)就是不让用数组了&#xff0c;然后强比较需要md5碰撞 ?md5_1%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc…...

【目标跟踪】跨相机如何匹配像素

文章目录 前言一、计算思路二、代码三、结果 前言 本本篇博客介绍一种非常简单粗暴的方法&#xff0c;做到跨相机像素匹配。已知各相机内外参&#xff0c;计算共视区域像素投影&#xff08;不需要计算图像特征&#xff09;。废话不多说&#xff0c;直接来&#xff0c;见下图。…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

Java - Mysql数据类型对应

Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

【Go语言基础【13】】函数、闭包、方法

文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数&#xff08;函数作为参数、返回值&#xff09; 三、匿名函数与闭包1. 匿名函数&#xff08;Lambda函…...

js 设置3秒后执行

如何在JavaScript中延迟3秒执行操作 在JavaScript中&#xff0c;要设置一个操作在指定延迟后&#xff08;例如3秒&#xff09;执行&#xff0c;可以使用 setTimeout 函数。setTimeout 是JavaScript的核心计时器方法&#xff0c;它接受两个参数&#xff1a; 要执行的函数&…...

基于Uniapp的HarmonyOS 5.0体育应用开发攻略

一、技术架构设计 1.混合开发框架选型 &#xff08;1&#xff09;使用Uniapp 3.8版本支持ArkTS编译 &#xff08;2&#xff09;通过uni-harmony插件调用原生能力 &#xff08;3&#xff09;分层架构设计&#xff1a; graph TDA[UI层] -->|Vue语法| B(Uniapp框架)B --&g…...