Go的标准库Context理解
作为一个才入门的菜鸟,还没写过真正的 go 项目,要理解这个 Context 还是有点难,不过还是要尝试一下。在 Go http包的Server中,每一个请求在都有一个对应的 goroutine 去处理。请求处理函数通常会启动额外的 goroutine 用来访问后端服务,比如数据库服务。用来处理一个请求的 goroutine 通常需要访问一些与请求特定的数据,比如终端用户的身份认证信息、验证相关的token、请求的截止时间。 当一个请求被取消或超时时,所有用来处理该请求的 goroutine 都应该迅速退出,然后系统才能释放这些 goroutine 占用的资源。
基本示例
基本示例中,本来想使用等待组来退出 goroutine ,奈何由于无限循环的原因,导致 wg.Done 根本没办法执行,也就导致主线程没版本 over 了。
package mainimport ("fmt""sync""time"
)var wg sync.WaitGroup// 初始的例子func worker() {for {fmt.Println("worker")time.Sleep(time.Second)}// 如何接收外部命令实现退出wg.Done()
}func main() {wg.Add(1)go worker()// 如何优雅的实现结束子goroutinewg.Wait()fmt.Println("over")
}
全局变量方式
为了让 goroutine 能正常退出,可以使用全局变量,将退出信号传递到 worker 里边,这样,worker 里的 wg.Done 就可以正常执行到了。但全局变量方式控制也有其缺陷,全局变量方式存在的问题:
- 使用全局变量在跨包调用时不容易统一。
- 如果worker中再启动goroutine,就不太好控制了。
package mainimport ("fmt""sync""time"
)var wg sync.WaitGroup
var exit bool// 全局变量方式存在的问题:
// 1. 使用全局变量在跨包调用时不容易统一
// 2. 如果worker中再启动goroutine,就不太好控制了。func worker() {for {fmt.Println("worker")time.Sleep(time.Second)if exit {break}}wg.Done()
}func main() {wg.Add(1)go worker()time.Sleep(3 * time.Second)exit = truewg.Wait()fmt.Println("over")
}
worker
worker
worker
over
通道方式
package mainimport ("fmt""sync""time"
)var wg sync.WaitGroup// 管道方式存在的问题:
// 1. 使用全局变量在跨包调用时不容易实现规范和统一,需要维护一个共用的channelfunc worker(exitChan chan struct{}) {
LOOP:for {fmt.Println("worker")time.Sleep(time.Second)select {case <-exitChan: // 等待接收上级通知break LOOPdefault:}}wg.Done()
}func main() {var exitChan = make(chan struct{})wg.Add(1)go worker(exitChan)time.Sleep(time.Second * 3) // sleep3秒以免程序过快退出exitChan <- struct{}{} // 给子goroutine发送退出信号close(exitChan)wg.Wait()fmt.Println("over")
}
worker
worker
worker
over
官方版本
package mainimport ("context""fmt""sync""time"
)var wg sync.WaitGroupfunc worker(ctx context.Context) {
LOOP:for {fmt.Println("worker")time.Sleep(time.Second)select {case <-ctx.Done(): // 等待上级通知break LOOPdefault:}}wg.Done()
}func main() {ctx, cancel := context.WithCancel(context.Background())wg.Add(1)go worker(ctx)time.Sleep(time.Second * 3)cancel() // 通知子goroutine结束wg.Wait()fmt.Println("over")
}
worker
worker
worker
over
当子goroutine又开启另外一个goroutine时,只需要将ctx传入即可。
package mainimport ("context""fmt""sync""time"
)var wg sync.WaitGroupfunc worker(ctx context.Context) {go worker2(ctx)
LOOP:for {fmt.Println("worker")time.Sleep(time.Second)select {case <-ctx.Done(): // 等待上级通知break LOOPdefault:}}wg.Done()
}func worker2(ctx context.Context) {
LOOP:for {fmt.Println("worker2")time.Sleep(time.Second)select {case <-ctx.Done(): // 等待上级通知break LOOPdefault:}}
}
func main() {ctx, cancel := context.WithCancel(context.Background())wg.Add(1)go worker(ctx)time.Sleep(time.Second * 3)cancel() // 通知子goroutine结束wg.Wait()fmt.Println("over")
}
With系列函数
WithCancel
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
WithCancel
返回带有新Done通道的父节点的副本。当调用返回的cancel函数或当关闭父上下文的Done通道时,将关闭返回上下文的Done通道,无论先发生什么情况。取消此上下文将释放与其关联的资源,因此代码应该在此上下文中运行的操作完成后立即调用cancel。
package mainimport ("context""fmt"
)func gen(ctx context.Context) <-chan int {dst := make(chan int)n := 1go func() {for {select {case <-ctx.Done():return // return结束该goroutine,防止泄露case dst <- n:n++}}}()return dst
}func main() {ctx, cancel := context.WithCancel(context.Background())defer cancel() // 当我们取完需要的整数后调用cancelfor n := range gen(ctx) {fmt.Println(n)if n == 5 {break}}
}
gen
函数在单独的goroutine中生成整数并将它们发送到返回的通道。 gen的调用者在使用生成的整数之后需要取消上下文,以免gen
启动的内部goroutine发生泄漏。
WithDeadline
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
返回父上下文的副本,并将deadline调整为不迟于d(相当于截止时间到达时,会主动关闭 Done 通道)。如果父上下文的deadline已经早于d,则WithDeadline(parent, d)在语义上等同于父上下文。当截止日过期时,当调用返回的cancel函数时,或者当父上下文的Done通道关闭时,返回上下文的Done通道将被关闭,以最先发生的情况为准。取消此上下文将释放与其关联的资源,因此代码应该在此上下文中运行的操作完成后立即调用cancel。
package mainimport ("context""fmt""time"
)func main() {d := time.Now().Add(50 * time.Millisecond)ctx, cancel := context.WithDeadline(context.Background(), d)// 尽管ctx会过期,但在任何情况下调用它的cancel函数都是很好的实践。// 如果不这样做,可能会使上下文及其父类存活的时间超过必要的时间。defer cancel()select {case <-time.After(1 * time.Second):fmt.Println("overslept")case <-ctx.Done():fmt.Println(ctx.Err())}
}
上面的代码中,定义了一个50毫秒之后过期的deadline,然后我们调用context.WithDeadline(context.Background(), d)
得到一个上下文(ctx)和一个取消函数(cancel),然后使用一个select让主程序陷入等待:等待1秒后打印overslept
退出或者等待ctx过期后退出。在上面的示例代码中,因为ctx 50毫秒后就会过期,所以ctx.Done()
会先接收到context到期通知,并且会打印ctx.Err()的内容。
WithTimeout
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
WithTimeout
返回 WithDeadline(parent, time.Now().Add(timeout))
。相当于通过当前时间和超时时间来定义了 deadline 。取消此上下文将释放与其相关的资源,因此代码应该在此上下文中运行的操作完成后立即调用cancel,通常用于数据库或者网络连接的超时控制
package mainimport ("context""fmt""sync""time"
)// context.WithTimeoutvar wg sync.WaitGroupfunc worker(ctx context.Context) {
LOOP:for {fmt.Println("db connecting ...")time.Sleep(time.Millisecond * 10) // 假设正常连接数据库耗时10毫秒select {case <-ctx.Done(): // 50毫秒后自动调用break LOOPdefault:}}fmt.Println("worker done!")wg.Done()
}func main() {// 设置一个50毫秒的超时ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)wg.Add(1)go worker(ctx)time.Sleep(time.Second * 5)cancel() // 通知子goroutine结束wg.Wait()fmt.Println("over")
}
db connecting ...
db connecting ...
db connecting ...
db connecting ...
db connecting ...
worker done!
over
WithValue
WithValue
返回父节点的副本,其中与key关联的值为val。仅对API和进程间传递请求域的数据使用上下文值,而不是使用它来传递可选参数给函数。所提供的键必须是可比较的,并且不应该是string
类型或任何其他内置类型,以避免使用上下文在包之间发生冲突。WithValue
的用户应该为键定义自己的类型。为了避免在分配给interface{}时进行分配,上下文键通常具有具体类型struct{}
。或者,导出的上下文关键变量的静态类型应该是指针或接口。
package mainimport ("context""fmt""sync""time"
)// context.WithValuetype TraceCode stringvar wg sync.WaitGroupfunc worker(ctx context.Context) {key := TraceCode("TRACE_CODE")traceCode, ok := ctx.Value(key).(string) // 在子goroutine中获取trace codeif !ok {fmt.Println("invalid trace code")}
LOOP:for {fmt.Printf("worker, trace code:%s\n", traceCode)time.Sleep(time.Millisecond * 10) // 假设正常连接数据库耗时10毫秒select {case <-ctx.Done(): // 50毫秒后自动调用break LOOPdefault:}}fmt.Println("worker done!")wg.Done()
}func main() {// 设置一个50毫秒的超时ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)// 在系统的入口中设置trace code传递给后续启动的goroutine实现日志数据聚合ctx = context.WithValue(ctx, TraceCode("TRACE_CODE"), "12345")wg.Add(1)go worker(ctx)time.Sleep(time.Second * 5)cancel() // 通知子goroutine结束wg.Wait()fmt.Println("over")
}
worker, trace code:12345
worker, trace code:12345
worker, trace code:12345
worker done!
over
相关文章:
Go的标准库Context理解
作为一个才入门的菜鸟,还没写过真正的 go 项目,要理解这个 Context 还是有点难,不过还是要尝试一下。在 Go http包的Server中,每一个请求在都有一个对应的 goroutine 去处理。请求处理函数通常会启动额外的 goroutine 用来访问后端…...

Nuxt3_1_路由+页面+组件+资源+样式 使用及实例
1、 简介 1.1 开发必备 node版本 v16.10.0 我使用的是16.14.0编辑器推荐使用Volar Extension 的VS code插件Terminal 运行nuxt指令 1.2 环境搭建 安装项目: npx nuxilatest init [first_nuxt3]进入项目目录: cd [first_nuxt3]安装依赖:n…...

Kafka第三课
Flume 由三部分 Source Channel Sink 可以通过配置拦截器和Channel选择器,来实现对数据的分流, 可以通过对channel的2个存储容量的的设置,来实现对流速的控制 Kafka 同样由三大部分组成 生产者 服务器 消费者 生产者负责发送数据给服务器 服务器存储数据 消费者通过从服务器取…...
elasticsearch修改es集群的索引副本数量
前言 最近es集群进行调整,从2节点变成了单节点。所以需要将集群模式改为单点模式,并需要将es 集群的全部索引副本个数改为0,不然会有很多未分配的分片,导致集群状态为 yellow。 具体实践 1. 先将现有的index的副本数量为0个 此…...
【SpringCloud】Ribbon定制化配置
文章目录 使用Ribbon自带负载均衡算法添加负载均衡算法ConfigurationRestTemplate使用上面负载均衡算法 自定义负载均衡算法负载均衡算法实现RestTemplate在Controller中使用该负载均衡算法ServiceIInstance解释 使用Ribbon自带负载均衡算法 添加负载均衡算法Configuration /…...
Mac terminal 每次打开都要重新配置文件
1. 问题描述 每次打开 Terminal,base_profile文件中配置的内容就不生效,需要重新执行source ~/.bash_profile才可以使用。 2. 原因分析 zsh加载的是~/.zshrc文件,而.zshrc 文件中并没有定义任务环境变量。 3. 解决办法 在~/.zshrc文件末尾添…...

el-button实现按钮,鼠标移入显示,移出隐藏
2023.8.18今天我学习了 如何实现鼠标移入显示按钮,鼠标移出隐藏按钮。 效果如图: 鼠标移入时: 鼠标移出时: mouseover //鼠标移入事件 mouseleave //鼠标移出事件 原本我是想直接在el-button写入这两个方法,但是elem…...

uniapp+uview封装小程序请求
提要: uniapp项目引入uview库 此步骤不再阐述 1.创建环境文件 env.js: let BASE_URL;if (process.env.NODE_ENV development) {// 开发环境BASE_URL 请求地址; } else {// 生产环境BASE_URL 请求地址; }export default BASE_URL; 2.创建请求文件 该…...

idea常见错误大全之:解决全局搜索失效+搜索条件失效(条件为空)+F8失灵
问题一:全局搜索快捷键ctrlshiftf 突然失灵了,键盘敲烂了 都没反应,这是为什么呢? 肯定不是idea本身的原因,那么就是其它外在因素影响到了idea的快捷键,那么其它的快捷键为什么没失效呢,原因只有…...

【论文阅读】基于深度学习的时序预测——LTSF-Linear
系列文章链接 论文一:2020 Informer:长时序数据预测 论文二:2021 Autoformer:长序列数据预测 论文三:2022 FEDformer:长序列数据预测 论文四:2022 Non-Stationary Transformers:非平…...

02.FFMPEG的安装和添加硬件加速自编译
说一个极其郁闷的事情,就在昨天收到3399的一块板子后,往电脑上面一插,然后悲剧的事情就发生了,我的电脑蓝屏重启了,这下好了,我写到一半的帖子也不见了,我的SSH里面的记录全部消失了,…...

elementUI 的上传组件<el-upload>,自定义上传按钮样式
方法一: 原理:调用<el-upload>组件的方法唤起选择文件事件 效果: 页面代码: 1、选择图片按钮 <div class"flex_row_spacebetween btn" click"chooseImg"><span class"el-icon-plus ic…...

【卷积神经网络】卷积,池化,全连接
随着计算机硬件的升级与性能的提高,运算量已不再是阻碍深度学习发展的难题。卷积神经网络(Convolution Neural Network,CNN)是深度学习中一项代表性的工作,CNN 是受人脑对图像的理解过程启发而提出的模型,其…...
【SA8295P 源码分析】76 - Thermal 功耗 之 /dev/thermalmgr 相关调试命令汇总
【SA8295P 源码分析】76 - Thermal 功耗 之 /dev/thermalmgr 相关调试命令汇总 1、配置文件:/mnt/etc/system/config/thermal-engine.conf2、获取当前SOC所有温度传感器的温度:cat /dev/thermalmgr3、查看所有 Thermal 默认配置和自定义配置:echo query config > /dev/th…...

以太网(一):PoE供电
一、定义: PoE系统包括供电端设备(PSE)和受电端设备(PD)两部分PoE(Power over Ethernet):是一种可以在以太网中透过双绞线来传输电力与数据到设备上的技术PSE(Power S…...

骨传导耳机游泳能戴吗?骨传导游泳耳机哪个牌子好?
溽热的夏日,如果能够跳入水中畅游一番,那真的是再好不过了,既能强身健体,又能降温解暑。公共的游泳场馆人声鼎沸,像我这种“社恐”患者,如果在场馆中要待好几个小时,难免会觉得时间漫长…...

18万字应急管理局智慧矿山煤矿数字化矿山技术解决方案WORD
导读:原文《18万字应急管理局智慧矿山煤矿数字化矿山技术解决方案WORD》(获取来源见文尾),本文精选其中精华及架构部分,逻辑清晰、内容完整,为快速形成售前方案提供参考。 目 录 第一章 项目概述 1.1项目…...

【mysql】MySQL CUP过高如何排查?
文章目录 一. 问题锁定二. QPS激增会导致CPU飘高三. 慢SQL会导致CPU飘高四. 大量空闲连接会导致CPU飘高五. MySQL问题排查常用命令 一. 问题锁定 通过top命令查看服务器CPU资源使用情况,明确CPU占用率较高的是否是mysqld进程,如果是则可以明确CUP飘高的原…...
lua实现http的异步回调
想用lua实现与http服务器的通信,请求一些数据会回来,默认lua.socket.http是同步的,所以想弄一个异步的方式 测试环境 lua 5.1 同步 以下是同步的代码,其中http.request会被阻塞住的 local function send_request()local res,…...

云服务 Ubuntu 20.04 版本 使用 Nginx 配置SSL证书和nginx从HTTP跳转到HTTPS
1.云服务申请免费的SSL证书 2.从云服务SSL证书下载到本地解压上传到服务器 3.配置Nginx下的 nginx.cof 文件 4.开放安全组,内部与外部 5.测试配置与跳转是否成功 1.云服务申请免费的SSL证书 1.1.登录云平台找到SSL证书 注意:博主这里是腾讯云&#x…...

RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...

使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...

Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...
JS手写代码篇----使用Promise封装AJAX请求
15、使用Promise封装AJAX请求 promise就有reject和resolve了,就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...
LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》
🧠 LangChain 中 TextSplitter 的使用详解:从基础到进阶(附代码) 一、前言 在处理大规模文本数据时,特别是在构建知识库或进行大模型训练与推理时,文本切分(Text Splitting) 是一个…...

热门Chrome扩展程序存在明文传输风险,用户隐私安全受威胁
赛门铁克威胁猎手团队最新报告披露,数款拥有数百万活跃用户的Chrome扩展程序正在通过未加密的HTTP连接静默泄露用户敏感数据,严重威胁用户隐私安全。 知名扩展程序存在明文传输风险 尽管宣称提供安全浏览、数据分析或便捷界面等功能,但SEMR…...
深入浅出WebGL:在浏览器中解锁3D世界的魔法钥匙
WebGL:在浏览器中解锁3D世界的魔法钥匙 引言:网页的边界正在消失 在数字化浪潮的推动下,网页早已不再是静态信息的展示窗口。如今,我们可以在浏览器中体验逼真的3D游戏、交互式数据可视化、虚拟实验室,甚至沉浸式的V…...

leetcode73-矩阵置零
leetcode 73 思路 记录 0 元素的位置:遍历整个矩阵,找出所有值为 0 的元素,并将它们的坐标记录在数组zeroPosition中置零操作:遍历记录的所有 0 元素位置,将每个位置对应的行和列的所有元素置为 0 具体步骤 初始化…...