Golang 中的 Context 包
简介
今天,我们将讨论 Go 编程中非常重要的一个主题:context
包。如果你现在觉得它很令人困惑,不用担心 — 在本文结束时,你将像专家一样处理 context!
想象一下,你在一个主题公园,兴奋地准备搭乘一座巨大的过山车。但有个问题:排队的人非常多,而且公园快要关门,你只有一个小时的时间。你会怎么办?嗯,你可能会等一会儿,但不会等一个小时,对吧?如果你等了 30 分钟还没有到前面,你会离开队伍去尝试其他游乐设施。这就是我们所谓的 ‘超时’。
现在,想象一下,你还在排队,突然下起了倾盆大雨。过山车的操作员决定关闭过山车。你不会继续排队等待根本不会发生的事情,对吧?你会立刻离开队伍。这就是我们所谓的 ‘取消’。
在编程世界中,我们经常面临类似的情况。我们要求程序执行可能需要很长时间或需要因某种原因停止的任务。这就是 context
包发挥作用的地方。它允许我们优雅地处理这些超时和取消。
它是如何工作的
**创建上下文:**我们首先创建一个上下文。这就像排队等待过山车一样。
ctx := context.Background() // This gives you an empty context
**设置超时:**接下来,我们可以在上下文中设置超时。这就好比你决定在排队多久后放弃并去尝试其他游乐设施。
ctxWithTimeout, cancel := context.WithTimeout(ctx, time.Second*10) // Wait for 10 seconds
// Don't forget to call cancel when you're done, or else you might leak resources!
defer cancel()
**检查超时:**现在,我们可以使用上下文来检查是否等待时间太长,是否应该停止我们的任务。这就好比在排队等待时看看手表。
select {
case <-time.After(time.Second * 15): // This task takes 15 secondsfmt.Println("Finished the task")
case <-ctxWithTimeout.Done():fmt.Println("We've waited too long, let's move on!") // We only wait for 10 seconds
}
**取消上下文:**最后,如果出于某种原因需要停止任务,我们可以取消上下文。这就好比听到因下雨而宣布过山车关闭。
cancel() // We call the cancel function we got when we created our context with timeout
示例 1:慢速数据库查询
想象一下构建一个从数据库中获取用户数据的Web应用程序。有时,数据库响应较慢,你不希望用户永远等下去。在这种情况下,你可以使用带有超时的上下文。
func getUser(ctx context.Context, id int) (*User, error) {// Create a new context that will be cancelled if it takes more than 3 secondsctx, cancel := context.WithTimeout(ctx, 3*time.Second)defer cancel()// Assume db.QueryRowContext is a function that executes a SQL query and returns a rowrow := db.QueryRowContext(ctx, "SELECT name FROM users WHERE id = ?", id)var name stringif err := row.Scan(&name); err != nil {return nil, err}return &User{Name: name}, nil
}
在这个示例中,如果数据库查询花费超过3秒的时间,上下文将被取消,db.QueryRowContext
应返回一个错误。
示例 2:网页抓取
假设你正在编写一个用于从网站抓取数据的程序。然而,该网站有时响应较慢,或者根本不响应。你可以使用上下文来防止你的程序陷入困境。
func scrapeWebsite(ctx context.Context, url string) (*html.Node, error) {// Create a new context that will be cancelled if it takes more than 5 secondsctx, cancel := context.WithTimeout(ctx, 5*time.Second)defer cancel()// Create a request with the contextreq, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)if err != nil {return nil, err}// Execute the requestresp, err := http.DefaultClient.Do(req)if err != nil {return nil, err}defer resp.Body.Close()// Parse the response body as HTMLreturn html.Parse(resp.Body), nill
}
在这个示例中,如果从网站获取数据超过5秒,上下文将被取消,http.DefaultClient.Do
应该返回一个错误。
示例3:长时间运行的任务
假设你有一个执行长时间运行任务的程序,但你希望能够在程序接收到关闭信号时停止任务。这在一个 Web 服务器中可能会很有用,当关闭时必须停止提供请求并进行清理。
func doTask(ctx context.Context) {for {select {case <-time.After(1 * time.Second):// The task is done, we're ready to exitfmt.Println("Task is done")returncase <-ctx.Done():// The context was cancelled from the outside, clean up and exitfmt.Println("Got cancel signal, cleaning up")return}}
}func main() {// Create a new contextctx, cancel := context.WithCancel(context.Background())// Start the task in a goroutinego doTask(ctx)// Wait for a shutdown signal<-getShutdownSignal()// Cancel the context, which will stop the taskcancel()// Wait for a bit to allow the task to clean uptime.Sleep(1 * time.Second)
}
在这个示例中,当程序接收到关闭信号时,它会取消上下文,这会导致 doTask
在 <-ctx.Done()
上接收到信号。
示例4:HTTP 服务器
假设你正在构建一个处理传入请求的 HTTP 服务器。一些请求可能需要很长时间来处理,你希望设置一个最长处理时间限制。
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)defer cancel()// Simulate a long-running operationselect {case <-time.After(3 * time.Second):w.Write([]byte("Operation finished."))case <-ctx.Done():w.Write([]byte("Operation timed out."))}
})http.ListenAndServe(":8080", nil)
在这个示例中,如果操作需要超过2秒的时间,上下文将被取消,并且服务器将响应“操作超时”。
示例5:同步多个 Goroutines
假设你正在编写一个程序,使用 Goroutines 并发执行多个任务。如果其中一个任务失败,你希望取消所有其他任务。
func doTask(ctx context.Context, id int) {select {case <-time.After(time.Duration(rand.Intn(4)) * time.Second):fmt.Printf("Task %v finished.\n", id)case <-ctx.Done():fmt.Printf("Task %v cancelled.\n", id)}
}func main() {ctx, cancel := context.WithCancel(context.Background())for i := 1; i <= 5; i++ {go doTask(ctx, i)}// Cancel the context after 2 secondstime.Sleep(2 * time.Second)cancel()// Give the tasks some time to finish uptime.Sleep(1 * time.Second)
}
在这个示例中,当上下文被取消时,仍在运行的任何任务都将收到 <-ctx.Done()
,从而允许它们进行清理并退出。
仍然在尝试理解吗?
当我第一次接触上下文时,我感到非常困惑,我提出了一个问题,即如果 select 前面的命令花费太长时间,那么我们永远无法检测到 取消,这是一个合理的问题。因此,我准备了另一个示例来详细解释这种情况。
package mainimport ("context""fmt""math/rand""time"
)func expensiveCalculation(ctx context.Context, resultChan chan<- int) {// Simulate a long-running calculationrand.Seed(time.Now().UnixNano())sleepTime := time.Duration(rand.Intn(20)+1) * time.Secondfmt.Printf("Calculation will take %s to complete\n", sleepTime)time.Sleep(sleepTime)select {case <-ctx.Done():// Context was cancelled, don't write to the channelreturndefault:// Write the result to the channelresultChan <- 42 // replace with your actual calculation result}
}func main() {// Create a context that will be cancelled after 10 secondsctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)defer cancel() // The cancel should be deferred so resources are cleaned upresultChan := make(chan int)// Start the expensive calculation in a separate goroutinego expensiveCalculation(ctx, resultChan)// Wait for either the result or the context to be doneselect {case res := <-resultChan:// Got the resultfmt.Printf("Calculation completed with result: %d\n", res)case <-ctx.Done():// Context was cancelledfmt.Println("Calculation cancelled")}
}
time.Sleep(sleepTime)
命令是阻塞的,将暂停 goroutine 的执行,直到指定的持续时间已过。这意味着 select
语句不会被执行,直到休眠时间已经过去。
然而,上下文的取消与 goroutine 内的执行是独立的。如果上下文的截止时间被超过或其 cancel()
函数被调用,它的 Done()
通道将被关闭。
在主 goroutine 中,您有另一个 select
语句,它将立即检测上下文的 Done()
通道是否已关闭,并在不等待 expensiveCalculation
goroutine 完成休眠的情况下打印 “Calculation cancelled”。
也就是说,expensiveCalculation
goroutine 将在休眠后继续执行,它将在尝试写入 resultChan
之前检查上下文是否已被取消。如果已被取消,它将立即返回。这是为了避免潜在的死锁,如果没有其他goroutine从 resultChan
读取。
如果需要昂贵的计算(在本例中由 time.Sleep
模拟)在取消时立即停止,您必须设计计算以周期性地检查上下文是否已取消。这通常在需要将计算分解为较小部分的情况下使用循环。如果计算不能分解,并需要一次运行完毕,那么很遗憾,在 Go 中无法提前停止它。
相关文章:

Golang 中的 Context 包
简介 今天,我们将讨论 Go 编程中非常重要的一个主题:context 包。如果你现在觉得它很令人困惑,不用担心 — 在本文结束时,你将像专家一样处理 context! 想象一下,你在一个主题公园,兴奋地准备…...

nginx服务器
nginx反向代理 nginx 反向代理的好处: 提高访问速度 因为nginx本身可以进行缓存,如果访问的同一接口,并且做了数据缓存, nginx就直接可把数据返回,不需要真正地访问服务端,从而提高访问速度。 进行负载均衡…...
电脑常用快捷键
一、Win键(徽标键) 以下是Windows操作系统中使用Win键(徽标键)结合A-Z的所有快捷方式描述: - Win A:打开操作中心,访问系统通知、快速设置和其他功能 - Win B:将焦点设置到任务栏…...

吴恩达《机器学习》8-3->8-4:模型表示I、模型表示II
8.3、模型表示I 一、大脑神经网络的基本原理 为了构建神经网络模型,首先需要理解大脑中的神经网络是如何运作的。每个神经元都可以被看作是一个处理单元或神经核,它包含多个输入(树突)和一个输出(轴突)。…...

数据结构-二叉树力扣题
目录 1.相同的树 2.二叉树中查找值为x的节点 3.单值二叉树 4.对称二叉树 5.二叉树的前序遍历 6.另一颗树的子树 层序遍历: 7.二叉树遍历 8.判断二叉树是否是完全二叉树 一个特殊的性质: 1.相同的树 题目链接:力扣(LeetC…...

node 第十八天 中间件express-session实现会话密钥
express-session 文档 express-session 一个简单的express会话中间件 使用场景 在一个系统中, 需要维持一个临时的与登录态无关的会话密钥 比如登录系统后, 请求某一个接口, 接口的行为与登录态无关, 也就是说任何人对接口的访问…...

【机器学习基础】机器学习入门(1)
🚀个人主页:为梦而生~ 关注我一起学习吧! 💡专栏:机器学习 欢迎订阅!后面的内容会越来越有意思~ 💡专栏介绍: 本专栏的第一篇文章,当然要介绍一下了~来说一下这个专栏的开…...

赶快来!程序员接单必须知道的六大注意事项!!!
花花世界迷人眼,增加实力多搞钱!对于咱程序员来说,搞钱的最好办法就是网上接单了,相信也有不少小伙伴已经在尝试了吧!但是如何正确的搞钱呢?其中的注意事项你真的了解吗? 本期就和小编一起来看…...

【C++】日期类实现,与日期计算相关OJ题
文章目录 日期类的设计日期计算相关OJ题HJ73 计算日期到天数转换KY111 日期差值KY222 打印日期KY258 日期累加 在软件开发中,处理日期是一项常见的任务。为了方便地操作日期,我们可以使用C编程语言来创建一个简单的日期类。在本文中,我们将介…...

前端404页面的制作
1、背景 前端开发经常遇到输入路径不存在的问题,为此,把之前项目的404拿出来供大家参考。代码很简单,适合新手入手,效果如下: 2、代码引用的是element-plus框架 <template><div><el-result icon"…...

深兰科技轮腿家用AI机器人荣获“2023年度城市更新科创大奖”
近日,“2023金砖论坛第五季金立方城市更新科创大会”在上海举行,会上发布了《第12届金砖价值榜》,深兰科技研发出品的轮腿式家用AI机器人(兰宝),因其AI技术的创新性应用,荣获了“2023年度城市更新科创大奖”。 在10月2…...
669.修剪二叉树
原题链接:669.修剪二叉树 全代码: class Solution { public:TreeNode* trimBST(TreeNode* root, int low, int high) {if (root nullptr ) return nullptr;if (root->val < low) {TreeNode* right trimBST(root->right, low, high); // 寻找符合区间[l…...

论文绘图-机器学习100张模型图
在现代学术研究和技术展示中,高质量的图表和模型结构图是至关重要的。这尤其在机器学习领域更为显著,一个领域以其复杂的算法和复杂的数据结构而闻名。机器学习是一种使用统计技术使计算机系统能够从数据中学习和改进其任务执行的方法,而有效…...

PHP项目学习笔记-萤火商城-增加一个模块(表涉及到的操作和文件)
背景 是在store的后台添加一个页面,显示的如满意度调查的页面 在router.config.js里面配置一个新的菜单 路径:yoshop2.0-store\src\config\router.config.js 代码如下,很简单,定义了这菜单点击的时候进入的页面,和下面…...
如何用Java设计自动售货机?
如何用Java设计自动售货机?是大多在高级Java开发人员面试中经常被问到的好问题之一。在典型的编码面试中,你会得到一个问题描述来开发一个售货机,在有限的时间内,通常2到3小时内,你需要在Java中编写设计文档、工作代码和单元测试。这种Java面试的一个关键优势是可以一次测试候…...

JAVA数据代码示例
首先,我们需要导入一些必要的Java库 java import java.net.URL; import java.net.HttpURLConnection; import java.io.BufferedReader; import java.io.InputStreamReader; 然后,我们可以创建一个URL对象,表示我们要爬取的网页的URL。 jav…...
github常用搜索指令
一、常用搜索指令 以下指令可分开用,也可组合使用 根据关键字搜索 in:name xx继上一步:指定开发语言 language:Java in:name XX language:Java继上一步,指定更新日期 pushed:>2022-06-06 in:name XX language:Java pushed:>2022-0…...

为什么esp8266刷入了固件,无法接受AT指令
我遇到的解决方法是:是串口调试助手出了问题。所以需要更换一个串口调试助手软件。 上面这个就是我换了的软件 在开发的时候,经常会遇到软件故障,导致正确的方法,但是没有效果,好比以前用盗版的8.7版本的Proteus模拟…...
Scala---字符串、集合
一、字符串 StringStringBuilder 可变string操作方法举例 比较:equals比较忽略大小写:equalsIgnoreCaseindexOf:如果字符串中有传入的assci码对应的值,返回下标 1./** 2.* String && StringBuilder 3.*/ 4.val str "abcd" 5.val s…...

Power Automate-当收到HTTP请求时触发流程
选择创建自动化云端流,点跳过 第一个操作搜索HTTP,点击当收到HTTP请求时 点击使用示例有效负载生成架构 写入JSON,点击完成 正文JSON架构就自动生成了,再点击左下角的显示高级选项 Method根据需求选择 可以选择JSON中的参数赋值给…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...

Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...

【免费数据】2005-2019年我国272个地级市的旅游竞争力多指标数据(33个指标)
旅游业是一个城市的重要产业构成。旅游竞争力是一个城市竞争力的重要构成部分。一个城市的旅游竞争力反映了其在旅游市场竞争中的比较优势。 今日我们分享的是2005-2019年我国272个地级市的旅游竞争力多指标数据!该数据集源自2025年4月发表于《地理学报》的论文成果…...
TCP/IP 网络编程 | 服务端 客户端的封装
设计模式 文章目录 设计模式一、socket.h 接口(interface)二、socket.cpp 实现(implementation)三、server.cpp 使用封装(main 函数)四、client.cpp 使用封装(main 函数)五、退出方法…...