当前位置: 首页 > 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;见下图。…...

Spark 之 入门讲解详细版(1)

1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室&#xff08;Algorithms, Machines, and People Lab&#xff09;开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目&#xff0c;8个月后成为Apache顶级项目&#xff0c;速度之快足见过人之处&…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

2.Vue编写一个app

1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

C++ 求圆面积的程序(Program to find area of a circle)

给定半径r&#xff0c;求圆的面积。圆的面积应精确到小数点后5位。 例子&#xff1a; 输入&#xff1a;r 5 输出&#xff1a;78.53982 解释&#xff1a;由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982&#xff0c;因为我们只保留小数点后 5 位数字。 输…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...

学校时钟系统,标准考场时钟系统,AI亮相2025高考,赛思时钟系统为教育公平筑起“精准防线”

2025年#高考 将在近日拉开帷幕&#xff0c;#AI 监考一度冲上热搜。当AI深度融入高考&#xff0c;#时间同步 不再是辅助功能&#xff0c;而是决定AI监考系统成败的“生命线”。 AI亮相2025高考&#xff0c;40种异常行为0.5秒精准识别 2025年高考即将拉开帷幕&#xff0c;江西、…...

Python Einops库:深度学习中的张量操作革命

Einops&#xff08;爱因斯坦操作库&#xff09;就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库&#xff0c;用类似自然语言的表达式替代了晦涩的API调用&#xff0c;彻底改变了深度学习工程…...