[Golang] Context

[Golang] Context

文章目录

  • [Golang] Context
    • 什么是context
    • 创建context
      • 创建根context
      • 创建context
    • context的作用
      • 并发控制
      • context.WithCancel
      • context.WithDeadline
      • context.WithTimeout
      • context.WithValue

什么是context

Golang在1.7版本中引入了一个标准库的接口context,定义:

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

它定义了四个方法:

  • Deadline:设置context.Context被取消的时间,即截止日期

  • Done:返回一个只读channel,当Context到达截止日期时或被取消,这个channel就会被关闭,表示Context的链路结束,多次调用Done会返回同一个channel

  • Err:返回Context结束的原因,它只会在Done返回的channel被关闭时,才会返回非空的值;

    • 情况1:Context被取消:返回Canceled
    • 情况2:Context超时:返回DeadlineExceeded
  • Value:从context.Context中获取键对应的值,类似与map的get方法,对于同一个Context,多次调用Value并传入相同的key会返回相同的结果,如果没有对应的key就返回nil。

    • 键值对通过WithValue方法写入
func WithValue(parent Context, key, val any) Context {if parent == nil {panic("cannot create context from nil parent")}if key == nil {panic("nil key")}if !reflectlite.TypeOf(key).Comparable() {panic("key is not comparable")}return &valueCtx{parent, key, val}
}

创建context

创建根context

两种方法:

  • context.Background()
  • context.TODO()

两者没有什么太多的区别,都是创建根context,根context是一个空的context,不具备任何功能。

一般情况下,当前函数没有上下文作为入参,我们就使用context.Background()创建一个根context作为起始的上下文向下传递。

创建context

根context被创建后,不具备任何功能,为了让context在程序中发挥作用,我们需要依靠包提供的With系列函数来进行派生。

四个派生函数:

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

基于当前context,每个With函数都会创建出一个新的context,这类似于我们熟悉的树结构,当前context称为父context,派生出的每个新context被称为子context。

image-20240918180659297

通过根context的四个With函数派生出四种类型的context,每种context又可以通过同样的方式调用with系列方法继续向下派生出新的context,整体结构像一个树一样。

context的作用

  • 用于并发控制,控制协程的退出
  • 上下文信息的传递

总的来说,就是用来在父子goroutine间进行值传递和发生cancel信号的一种机制。

并发控制

一般的服务器都是一直运行的,等待客户端或者浏览器的请求做出响应,思考这种场景,一个微服务架构中下,服务器收到一个请求后,并不会在一个goroutine下完成(如果逻辑复杂),而是创建很多goroutine共同完成这个请求。

假设有rpc1—rpc2—rpc3—rpc4—rpc5,5个rpc调用。

但是如果在整个rpc调用中,如果rpc1就出现了错误,如果没有context存在,服务器就会坚持调用完整个流程,也就是等待所有rpc调用完成后才能返回结果,但是实际上这样浪费了不少的时间,单纯浪费计算和IO资源(rpc1错误之后的rpc调用都是无用功)。因为rpc调用之间不知道已经产生了错误,而context就很好的解决了这个问题。

在不需要子goroutine继续执行的时候,通过context通知子goroutine关闭即可。

context.WithCancel

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {c := withCancel(parent)return c, func() { c.cancel(true, Canceled, nil) }
}

context.WithCancel函数是一个取消控制函数,只需要一个context作为参数,能够衍生出一个新的子context和取消函数Cancel,我们可以通过这个将这个子context传入子goroutine中,执行Cancel函数来关闭这个子goroutine,当前的上下文和它的子上下文都会被取消,所有的goroutine都会同步收到取消信号。

示例:

package mainimport ("context""fmt""time"
)func main() {fmt.Println()ctx, cancel := context.WithCancel(context.Background())go watch(ctx, "goroutine1")go watch(ctx, "goroutine2")time.Sleep(3 * time.Second)fmt.Println("end!!!")cancel()time.Sleep(time.Second)
}
func watch(ctx context.Context, name string) {for {select {case <-ctx.Done():fmt.Println(name, " exit")returndefault:fmt.Println(name, " watching")time.Sleep(time.Second)}}
}

执行结果:

image-20240918184141136

通过WithCancel函数派生出一个带有返回函数cancel的ctx:ctx, cancel := context.WithCancel(context.Background()),并且把ctx传入子goroutine中,在3秒内没有执行cancel,子goroutine将一直执行default语句,3秒后,执行cancel,此时子goroutine从ctx.Done()收到消息,执行return结束。

context.WithDeadline

func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {return WithDeadlineCause(parent, d, nil)
}

context.WithDeadline函数也是一个取消控制函数,共有两个参数,一个是context,另一个是截止时间,同样会返回一个子context和取消函数cancel。在使用时,如果没有到截止日期,我们可以通过调用cancel函数来手动取消context,控制goroutine的退出,如果到了截止日期,我们都没有调用cancel函数,子context的Done()管道也会收到一个取消信号,来控制子goroutine的退出。

示例:

package mainimport ("context""fmt""time"
)func main() {fmt.Println()ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(3*time.Second))defer cancel()go watch(ctx, "goroutine1")go watch(ctx, "goroutine2")// 让goroutine1和goroutine2先执行5秒time.Sleep(5 * time.Second)fmt.Println("end!!!")}
func watch(ctx context.Context, name string) {for {select {case <-ctx.Done()://但是不到5秒,3秒时收到了退出信号fmt.Println(name, " exit")returndefault:fmt.Println(name, " watching")time.Sleep(time.Second)}}
}

执行结果:

image-20240918210655210

我们并没有调用cancel函数,但是在过了3秒后,子goroutine里ctx.Done()收到了信号,子goroutine进行退出。

context.WithTimeout

func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {return WithDeadline(parent, time.Now().Add(timeout))
}

context.WithTimeoutcontext.WithDeadline差不多,都是用于超时取消子context,只是第二个参数有点区别,不是具体时间,而是时间长度。

示例:

package mainimport ("context""fmt""time"
)func main() {fmt.Println()ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)defer cancel()go watch(ctx, "goroutine1")go watch(ctx, "goroutine2")// 让goroutine1和goroutine2先执行5秒time.Sleep(5 * time.Second)fmt.Println("end!!!")}
func watch(ctx context.Context, name string) {for {select {case <-ctx.Done()://但是不到5秒,3秒时收到了退出信号fmt.Println(name, " exit")returndefault:fmt.Println(name, " watching")time.Sleep(time.Second)}}
}

执行结果:

image-20240918211051169

执行结果和context.WithDeadline类似。

context.WithValue

func WithValue(parent Context, key, val any) Context {if parent == nil {panic("cannot create context from nil parent")}if key == nil {panic("nil key")}if !reflectlite.TypeOf(key).Comparable() {panic("key is not comparable")}return &valueCtx{parent, key, val}
}

context.WithValue函数从父context中创建一个子context用于传值,函数参数是父context、key、val,返回一个context。一般用于上下文信息的传递,比如请求唯一id,以及trace_id,用于链路追踪以及配置穿透。

示例:

package mainimport ("context""fmt""time"
)func main() {fmt.Println()ctx := context.WithValue(context.Background(), "name", "张三")go func1(ctx)time.Sleep(time.Second)
}
func func1(ctx context.Context) {fmt.Println("name = ", ctx.Value("name").(string))
}

执行结果:

image-20240918211613966

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

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

相关文章

【Web】初识Web和Tomcat服务器

目录 前言 一、认识web 1. 软件架构模式 2. web资源 3. URL请求路径&#xff08;统一资源定位符&#xff09; 二、Tomcat服务器 1. 简介 2. tomcat服务器的目录结构 3.使用tomcat服务器启动失败的常见原因 3.1 端口冲突 3.2 jdk环境变量配置出错 三、使用Tomcat发布…

Python_面向对象属性与方法

Python完全采用了面向对象的思想&#xff0c;是真正面向对象的编程语言&#xff0c;完全支持面向对象的基本功能&#xff0c;例如&#xff1a;继承、多态、封装等。Python中&#xff0c;一切皆对象。我们在前面学习的数据类型、函数等&#xff0c;都是对象。 面向过程和面向对象…

Java | Leetcode Java题解之第430题扁平化多级双向链表

题目&#xff1a; 题解&#xff1a; class Solution {public Node flatten(Node head) {dfs(head);return head;}public Node dfs(Node node) {Node cur node;// 记录链表的最后一个节点Node last null;while (cur ! null) {Node next cur.next;// 如果有子节点&#xff0…

【最基础最直观的排序 —— 选择排序算法】

最基础最直观的排序 —— 选择排序算法 选择排序算法是一种简单直观的排序算法。其基本思想是每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;存放在序列的起始位置&#xff0c;然后&#xff0c;再从剩余未排序元素中继续寻找最小&a…

【JS】Reflect

对象基本方法 JS语法操作对象时&#xff0c;本质上是调用一个内部封装好的函数&#xff0c;该函数中又会调用对象的基本方法&#xff0c;通过官方文档可以看到基本方法。在过去&#xff0c;这些对象的基本方法是不会对外暴露的。 如下面这段代码&#xff0c;使用JS语法给对象赋…

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-20

计算机前沿技术-人工智能算法-大语言模型-最新论文阅读-2024-09-20 1. Multimodal Fusion with LLMs for Engagement Prediction in Natural Conversation Authors: Cheng Charles Ma, Kevin Hyekang Joo, Alexandria K. Vail, Sunreeta Bhattacharya, Alvaro Fern’andez Ga…

网络层协议——IP

目录 IP层 IP报文格式 IP的理解 运营商 分片与组装 IP层 传输层的TCP或者UDP协议能直接将数据发送到网络中吗&#xff1f;显然不能&#xff0c;封装完的TCP报文还是需要向下交付&#xff0c;经过协议栈&#xff0c;从链路层发送到物理层也就是网路中。 那么tcp做了什么工…

9.创新与未来:ChatGPT的新功能和趋势【9/10】

创新与未来&#xff1a;ChatGPT的新功能和趋势 引言 在探讨人工智能的发展历程时&#xff0c;我们可以看到它已经从早期的图灵机和人工神经网络模型&#xff0c;发展到了今天能够模拟人类智能的复杂系统。人工智能的起源可以追溯到20世纪40年代&#xff0c;而它的重要里程碑包…

【ARM】MDK-当选择AC5时每次点击build都会全编译

1、 文档目标 解决MDK中选择AC5时每次点击build都会全编译 2、 问题场景 在MDK中点击build时&#xff0c;正常会只进行增量编译&#xff0c;但目前每次点击的时候都会全编译。 3、软硬件环境 1 软件版本&#xff1a;Keil MDK 5.38a 2 电脑环境&#xff1a;Window 10 4、解决…

centos7 配置 docker 国内镜像源

1.修改配置文件/etc/docker/daemon.json sudo vim /etc/docker/daemon.json2.增加或修改以下配置内容 {"registry-mirrors": ["https://dockerproxy.com","https://hub-mirror.c.163.com","https://mirror.baidubce.com","http…

网页爬虫法律与道德:探索法律边界与道德规范

目录 引言 一、网络爬虫技术概述 1.1 定义与功能 1.2 技术原理 1.3 案例分析 二、网络爬虫的法律边界 2.1 合法性要求 2.2 刑事风险 2.3 案例分析 三、网络爬虫的道德规范 3.1 尊重版权和隐私 3.2 合理使用爬虫技术 3.3 透明度和社会责任 四、技术挑战与应对策略…

面试经典 150 题:力扣88. 合并两个有序数组

每周一道算法题启动 题目 【题目链接】 【解法一】合并后排序 排序后的数组自动省略0的数字&#xff0c;又学到了 class Solution { public:void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {//合并两个数组后排序for(int i0; i<…

傅里叶变换及其应用笔记

傅里叶变换 预备知识学习路线扼要描述两者之间的共同点&#xff1a;线性运算周期性现象对称性与周期性的关系周期性 预备知识 学习路线 从傅里叶级数&#xff0c;过度到傅里叶变换 扼要描述 傅里叶级数&#xff08;Fourier series&#xff09;&#xff0c;几乎等同于周期性…

面经 | ES6

ES6 ES6Promise对象创建Promise三个状态resolve/reject 和微任务的关系await set vs weakSetmap vs weakMap ES6 Promise对象 new Promise(excutor);excutor是一个函数,会立刻执行;then里的回调函数&#xff0c;会进入微任务队列&#xff1b;then会返回一个新的promise对象aw…

LeetCode 面试经典150题 137.只出现一次的数字II

题目&#xff1a; 给你一个整数数组 nums &#xff0c;除某个元素仅出现 一次 外&#xff0c;其余每个元素都恰出现 三次 。请你找出并返回那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法且使用常数级空间来解决此问题。 思路&#xff1a; 方法一&#xf…

Java | Leetcode Java题解之第435题无重叠区间

题目&#xff1a; 题解&#xff1a; class Solution {public int eraseOverlapIntervals(int[][] intervals) {if (intervals.length 0) {return 0;}Arrays.sort(intervals, new Comparator<int[]>() {public int compare(int[] interval1, int[] interval2) {return i…

如何把python(.py或.ipynb)文件打包成可运行的.exe文件?

将 Python 程序打包成可执行的 .exe 文件&#xff0c;通常使用工具如 PyInstaller。这是一个常用的 Python 打包工具&#xff0c;可以将 Python 程序打包成独立的可执行文件&#xff0c;即使没有安装 Python 也能运行。 步骤&#xff1a; 1. 安装 PyInstaller 使用 conda 安…

风力发电机叶片表面缺陷识别检测数据集yolo数据集 共7000张

风力发电机叶片表面缺陷识别检测数据集yolo数据集 共7000张 风力发电机叶片表面缺陷识别数据集&#xff08;Wind Turbine Blade Defects Recognition Dataset, WTBDRD&#xff09; 摘要 WTBDRD 是一个专门为风力发电机叶片表面缺陷识别而设计的数据集&#xff0c;旨在为相关领…

HttpServletRequest简介

HttpServletRequest是什么&#xff1f; HttpServletRequest是一个接口&#xff0c;其父接口是ServletRequest&#xff1b;HttpServletRequest是Tomcat将请求报文转换封装而来的对象&#xff0c;在Tomcat调用service方法时传入&#xff1b;HttpServletRequest代表客户端发来的请…

HTML5好看的水果蔬菜在线商城网站源码系列模板2

文章目录 1.设计来源1.1 主界面1.2 商品列表界面1.3 商品详情界面1.4 其他界面效果 2.效果和源码2.1 动态效果2.2 源代码 源码下载 作者&#xff1a;xcLeigh 文章地址&#xff1a;https://blog.csdn.net/weixin_43151418/article/details/142059220 HTML5好看的水果蔬菜在线商城…