Go语言进阶之Context控制并发

Context

Context是Go语言中一个用于传递请求范围的上下文信息的标准库包,其主要用于处理并发操作中请求的生命周期的管理。

协程如何退出

利用协程退出的例子来说明Context的作用,以及没有使用Context,应该如何在没有执行完代码时提前退出协程

package mainimport ("fmt""time"
)func main() {// 创建一个用于退出的信号 channelexitChan := make(chan struct{})// 启动一个协程go func() {for {select {case <-exitChan:fmt.Println("协程收到退出信号,正在退出...")return // 退出协程default:// 模拟一些工作fmt.Println("协程正在执行...")time.Sleep(1 * time.Second) // 假装在做事情}}}()// 主协程等待一段时间后发送退出信号time.Sleep(5 * time.Second)close(exitChan) // 发送退出信号// 等待一段时间,确保协程能够退出time.Sleep(1 * time.Second)fmt.Println("主协程结束")
}

这段代码使用了for select循环来中途暂停协程运行

当我们启动了一个处主协程之外的协程时,我们可以通过for select循环来选择停止协程与继续协程

虽然这段代码看上去并不长,并且十分好用,但现实中肯定不止这一个协程,如果想同时让很多个协程停止那么代码将会很长,所以这时就要使用Context了。

Context使用示例

将上面的代码使用Context库进行改造

package mainimport ("context""fmt""time"
)func main() {// 创建一个带取消功能的上下文ctx, cancel := context.WithCancel(context.Background())defer cancel() // 确保在 main 结束时调用取消// 启动一个协程go func(ctx context.Context) {for {select {case <-ctx.Done():fmt.Println("协程收到退出信号,正在退出...")return // 退出协程default:// 模拟一些工作fmt.Println("协程正在执行...")time.Sleep(1 * time.Second) // 假装在做事情}}}()// 主协程等待一段时间后发送退出信号time.Sleep(5 * time.Second)cancel() // 发送退出信号// 等待一段时间,确保协程能够退出time.Sleep(1 * time.Second)fmt.Println("主协程结束")
}

在这段代码中使用 context.WithCancel创建一个可取消的上下文,其中cancel函数用于取消上下文。

当cancel函数被调用时,ctx.Done()会接收到结束的信号,并传递给case让进程结束。

Context同时处理多个协程

package mainimport ("context""fmt""sync""time"
)func main() {// 创建一个带取消功能的上下文ctx, cancel := context.WithCancel(context.Background())defer cancel() // 确保在 main 结束时调用取消var wg sync.WaitGroup// 启动多个协程numGoroutines := 3for i := 1; i <= numGoroutines; i++ {wg.Add(1) // 增加 WaitGroup 计数go func(id int, ctx context.Context) {defer wg.Done() // 协程完成时调用 Donefor {select {case <-ctx.Done():fmt.Printf("协程 %d 收到退出信号,正在退出...\n", id)return // 退出协程default:// 模拟一些工作fmt.Printf("协程 %d 正在执行...\n", id)time.Sleep(1 * time.Second) // 假装在做事情}}}(i, ctx)}// 主协程等待一段时间后发送退出信号time.Sleep(5 * time.Second)cancel() // 发送退出信号// 等待所有协程完成wg.Wait()fmt.Println("所有协程已结束,主协程结束")
}

Context详解

Context接口

Context接口方法主要有4种

1. Deadline() (deadline time.Time,ok bool)
// 这个方法可以获取设置的截止时间,第一个返回值deadline为截止时间,到了这个时间点,Context会自动发起取消请求,第二个返回值ok表示是否设置了截止时间2. Done() <-chan struct{}
// 这个方法返回一个只读的通道,当上下文被取消时,这个通道就会被关闭。当方法返回的chan可以读取时,则意味着Context已经发起了取消信号。通过Done方法收到这个信号之后,就可以做清理操作,然后退出协程,释放资源3. Err() error
// 这个方法返回上下文的错误状态,如果上下文被取消,返回context.Canceled;如果超时返回context.DeadlineExceeded4. Value(key interface{}) interface{}
// 从上下文中获取与特定键关联的值,Value方法获取该Context上绑定的值,是一个键值对,所以要通过Key才可以获取。

上下文的创建(Context树)

上下文树的基本结构:

根上下文:通常使用context.Background() 或 context.TODO() 作为树的根节点。

子上下文:通过context.WithCancel(), context.WithTimeout(), 或 context.WithDeadline()创建的上下文是根上下文或其他上下文的子上下文。

上下文函数详解

Context主要提供了5种方法来创建新的上下文:

1. context.Background()
// 返回一个空上下文,通常做根上下文,通常在程序的最顶层使用,它可以作为其他上下文的父上下文2. context.TODO()
// 当不确定使用哪个上下文时,可以使用TODO(),这个上下文的用途通常在代码开发的过程中,表示你需要稍后处理的上下文。3. context.WithCancel(parent Context)
// 创建一个可取消的上下文,返回一个新上下文和一个取消函数。调用取消函数会取消这个上下文及其所以子上下文。4. context.WithTimeout(parent Context,timeout time.Duration)
//创建一个带有超时的上下文。当超时时间达到,自动取消上下文5. context.WithDeadline(parent Context,deadline time.Time)
// 与WithTimeout类似,但是使用绝对时间来设置截止时间
Context树的传播

在Context树中,父上下文的状态会影响到所有子上下文,当父上下文被取消是,所以的子上下嗯也会自动被取消。

package mainimport ("context""fmt""time"
)func main() {// 创建根上下文rootCtx, cancel := context.WithCancel(context.Background())defer cancel()// 创建子上下文childCtx, childCancel := context.WithTimeout(rootCtx, 2*time.Second)defer childCancel()go func(ctx context.Context) {select {case <-ctx.Done():fmt.Println("子上下文被取消:", ctx.Err())}}(childCtx)// 模拟一些工作time.Sleep(1 * time.Second)// 取消根上下文cancel()// 等待子协程结束time.Sleep(1 * time.Second)
}

Context传值

Context在go中的作用不仅可以用于取消协程,还可以传值,通过这个能力Context储存的值可以供其他协程使用,这个方式适合传递请求范围内的共享数据。

context的值是通过context.WithValue函数设置的,其中传递的值是不可变的,并且使用interface{}类型实现

package mainimport ("context""fmt"
)type key stringconst userKey key = "user"func main() {// 创建一个背景上下文ctx := context.Background()// 将值存入上下文ctx = context.WithValue(ctx, userKey, "Alice")// 在 goroutine 中使用上下文go func(ctx context.Context) {// 从上下文中获取值if user, ok := ctx.Value(userKey).(string); ok {fmt.Println("User from context:", user)} else {fmt.Println("User not found in context")}}(ctx)// 等待 goroutine 完成// 在实际应用中,使用 sync.WaitGroup 或其他同步机制select {}
}

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

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

相关文章

挖掘web程序中的OAuth漏洞:利用redirect_uri和state参数接管账户

本文探讨了攻击者如何利用OAuth漏洞&#xff0c;重点是滥用redirect_uri和state参数以接管用户账户。如果redirect_uri参数验证不严&#xff0c;可能会导致未经授权的重定向到恶意服务器&#xff0c;从而使攻击者能够捕获敏感信息。同样&#xff0c;state参数的错误实现可能使O…

数据中心类DataCenter(二)

数据中心类DataCenter&#xff08;二&#xff09; 前言 在上一集我们对数据中心类DataCenter做了以下内容&#xff0c;我们对他进行设置单例模式&#xff0c;我们讨论并写入了一些我们数据中心类需要管理的数据&#xff0c;重点介绍了我们验证码id的重要性&#xff0c;在最后…

ORACLE 闪回技术简介

闪回技术是若干技术的集合 包含对数据库整体的闪回 对表的闪回 对事务的闪回 经典面试题面试题&#xff1a;简述Oracle数据库闪回技术&#xff1f; 1.闪回Oracle数据库 2.闪回表 3.闪回事务 数据库闪回 要想实现数据库闪回 1.必须配置数据库的恢复区 SQL> show parameter …

Python世界:力扣题解1712,将数组分成三个子数组的方案数,中等

Python世界&#xff1a;力扣题解1712&#xff1a;将数组分成三个子数组的方案数&#xff0c;中等 任务背景思路分析代码实现测试套件本文小结 任务背景 问题来自力扣题目1712. Ways to Split Array Into Three Subarrays&#xff0c;大意如下&#xff1a; A split of an intege…

Java集合框架之映射(Map)

引言 在Java编程中&#xff0c;管理键值对数据是一项常见的任务。Java集合框架中的Map接口为此提供了强大的支持。Map接口允许我们存储键值对&#xff0c;并提供了丰富的方法来操作这些键值对。本文将详细介绍Map接口的内部机制、特性、操作方法以及在实际编程中的应用场景。 …

Java集合基础——针对实习面试

目录 Java集合基础什么是Java集合&#xff1f;说说List,Set,Queue,Map的区别&#xff1f;说说List?说说Set?说说Map&#xff1f;说说Queue?为什么要用集合&#xff1f;如何选用集合&#xff1f; Java集合基础 什么是Java集合&#xff1f; Java集合&#xff08;Java Collect…

基于单片机的客车载客状况自动检测系统(论文+源码)

1系统整体设计 本课题为客车载客状况自动检测系统&#xff0c;在此以STM32单片机为核心控制器&#xff0c;结合压力传感器、红外传感器、蜂鸣器、语音提示模块、继电器、液晶等构成整个客车载客状况自动检测系统&#xff0c;整个系统架构如图2.1所示&#xff0c;在此通过两个红…

卷积核参数详细介绍

卷积核参数详细介绍如下&#xff1a; 2. 尺寸&#xff1a;卷积核通常是一个小矩阵&#xff0c;如3x3、5x5等&#xff0c;定义了卷积的大小范围&#xff0c;在网络中代表感受野的大小。 3. 权重&#xff1a;卷积核中的每个元素都有一个权重值&#xff0c;用于捕捉输入数据的特征…

第 3 章 -GO语言 基本语法

1. 注释 在编程中&#xff0c;注释是帮助理解代码的重要工具。Go语言支持两种类型的注释&#xff1a; 单行注释&#xff1a;以 // 开头&#xff0c;直到行尾都是注释。多行注释&#xff1a;以 /* 开始&#xff0c;以 */ 结束&#xff0c;可以跨越多行。 示例 package maini…

渗透测试(socket,namp,scapy)

socket:可以用来实现不同虚拟机或者不同计算机之间的通信。 socket常用函数&#xff1a; sock.bind(host,port) //host可接受client范围&#xff0c;以及连接的端口 sock.listen()//sever开启监听连接 sock.accpet()//返回 sock&#xff0c;addr 用来接受和发送数据 addr…

【mongodb】数据库的安装及连接初始化简明手册

NoSQL(NoSQL Not Only SQL )&#xff0c;意即"不仅仅是SQL"。 在现代的计算系统上每天网络上都会产生庞大的数据量。这些数据有很大一部分是由关系数据库管理系统&#xff08;RDBMS&#xff09;来处理。 通过应用实践证明&#xff0c;关系模型是非常适合于客户服务器…

内网对抗-信息收集篇SPN扫描DC定位角色区域定性服务探针安全防护凭据获取

知识点&#xff1a; 1、信息收集篇-网络架构-出网&角色&服务&成员 2、信息收集篇-安全防护-杀毒&防火墙&流量监控 3、信息收集篇-密码凭据-系统&工具&网站&网络域渗透的信息收集&#xff1a; 在攻防演练中&#xff0c;当完成边界突破后进入内…

OpenWebUI,RAG+外部知识库+AI写文的开源应用

引言 自从去年AI火起来之后&#xff0c;很多人便热衷于寻找适合自用的AI开源项目&#xff0c;把各家大模型API接入到自己的AI程序里&#xff0c;便可以通过AI辅助完成一系列日常任务&#xff0c;比如内容翻译/润色/总结/撰写、格式转换、数据分类、代码分析、角色扮演等等。 …

qt QErrorMessage详解

1、概述 QErrorMessage是Qt框架中用于显示错误消息的一个对话框类。它提供了一个简单的模态对话框&#xff0c;用于向用户显示错误或警告消息。QErrorMessage通常用于应用程序中&#xff0c;当需要向用户报告错误但不希望中断当前操作时。它提供了一个标准的错误消息界面&…

洛谷 P1622 释放囚犯(区间dp)

题目链接 https://www.luogu.com.cn/problem/P1622 思路 d p [ i ] [ j ] dp[i][j] dp[i][j]表示释放区间 [ i , j ] [i,j] [i,j]的罪犯所需的最小的肉&#xff0c;状态转移方程为&#xff1a; d p [ i ] [ j ] m i n ( d p [ i ] [ j ] , d p [ i ] [ k − 1 ] d p [ k 1…

一文了解Android的Doze模式

Android 的 Doze 模式是一项省电功能&#xff0c;主要用于减少设备的功耗&#xff0c;特别是在屏幕关闭且设备长时间未被使用的情况下。Doze 模式在 Android 6.0&#xff08;API Level 23&#xff09;首次引入&#xff0c;并在后续版本中不断改进&#xff0c;以便更智能地管理后…

美团代付微信小程序系统 read.php 任意文件读取漏洞复现

0x01 产品简介 美团代付微信小程序系统是美团点评旗下的一款基于微信小程序技术开发的应用程序功能之一,它允许用户方便快捷地请求他人为自己支付订单费用。随着移动支付的普及和微信小程序的广泛应用,美团作为中国领先的本地生活服务平台,推出了代付功能,以满足用户多样化…

SpringBoot-员工管理系统(1)

目录 一、首页配置 二、国际化 2.1 配置文件编写 2.2 使配置文件生效 2.3 配置页面国际化值 2.4 实现根据按钮自动切换中英文 三、登录拦截器 一、首页配置 1、所有页面的静态资源都需要用thymeleaf接管 2、url使用{}格式 二、国际化 2.1 配置文件编写 1、在resourc…

Git - 命令杂谈 - reset、revert和clean

记录一些平时常用的Git命令 reset # 操作HEAD的指向&#xff0c;修改当前分支仓库区提交树 git reset option commitID--soft&#xff0c;不碰索引区和工作区&#xff0c;只修改HEAD指向--mixed&#xff0c;不碰工作区&#xff0c;重置索引区&#xff0c;修改HEAD指向--hard&a…

JavaSE:初识Java(学习笔记)

java是高级语言的面向对象语言 .[最贴近生活.最快速分析和设计程序] 一&#xff0c;计算机语言发展历史 二&#xff0c;Java体系结构 1&#xff0c;JavaSE&#xff08;Java Standard Edition&#xff09; 标准版&#xff0c;定位在个人计算机上的应用 这个版本是Jav…