一篇文章搞懂Go语言中的Context
来源:脚本之家
2023-01-01 19:27:16
0浏览
收藏
本篇文章主要是结合我之前面试的各种经历和实战开发中遇到的问题解决经验整理的,希望这篇《一篇文章搞懂Go语言中的Context》对你有很大帮助!欢迎收藏,分享给更多的需要的朋友学习~
0 前置知识sync.WaitGroup
sync.WaitGroup是等待一组协程结束。它实现了一个类似任务队列的结构,可以向队列中加入任务,任务完成后就把任务从队列中移除,如果队列中的任务没有全部完成,队列就会触发阻塞以阻止程序继续运行。 sync.WaitGroup只有3个方法,Add(),Done(),Wait() 。
其中Done()是Add(-1)的别名,使用Add()添加计数,Done()减掉一个计数,计数不为0, 阻塞Wait()的运行。
示例:
package main import ( "fmt" "sync" "time" ) var group sync.WaitGroup func sayHello() { for i := 0; i <p><strong>效果:</strong></p> <p style="text-align:center"><img alt="" src="/uploads/20230101/167257355263b17270574da.png"></p> <h2>1 简介</h2> <p>在 Go 服务器中,每个传入请求都在其自己的 goroutine 中处理。请求处理程序通常会启动额外的 goroutine 来访问后端,例如数据库和 RPC 服务。处理请求的一组 goroutine 通常需要访问特定于请求的值,例如最终用户的身份、授权令牌和请求的截止日期。当请求被取消或超时时,处理该请求的所有 goroutine 都应该快速退出,以便系统可以回收它们正在使用的任何资源。</p> <p>为此,开发了一个<code>context</code>包,可以轻松地将请求范围的值、取消信号和截止日期跨 API 边界传递给处理请求所涉及的所有 goroutine。</p> <p>Context携带一个截止日期、一个取消信号和其他跨越API边界的值。上下文的方法可以被多个gor例程同时调用。</p> <p>对服务器的传入请求应该创建一个上下文,对服务器的传出调用应该接受一个上下文。它们之间的函数调用链必须传播 Context,可选择将其替换为使用 WithCancel、WithDeadline、WithTimeout 或 WithValue 创建的派生 Context。当一个上下文被取消时,所有从它派生的上下文也被取消。</p> <p>WithCancel、WithDeadline 和 WithTimeout 函数采用 Context(父)并返回派生的 Context(子)和 CancelFunc。调用 CancelFunc 会取消子项及其子项,删除父项对子项的引用,并停止任何关联的计时器。调用 CancelFunc 失败会泄漏子项及其子项,直到父项被取消或计时器触发。go vet 工具检查是否在所有控制流路径上使用了 CancelFuncs。</p> <p>使用上下文的程序应遵循以下规则,以保持跨包的接口一致,并启用静态分析工具来检查上下文传播:</p> <p>不要将上下文存储在结构类型中;相反,将 Context 显式传递给需要它的每个函数。</p> <p><strong>Context 应该是第一个参数,通常命名为 ctx:</strong></p> <pre class="brush:go;">func DoSomething(ctx context.Context, arg Arg) error { // ... 使用 ctx ... }
即使函数允许,也不要传递 nil 上下文。如果不确定要使用哪个 Context,请传递 context.TODO。
仅将上下文值用于传输流程和 API 的请求范围数据,而不用于将可选参数传递给函数。
相同的 Context 可以传递给在不同的 goroutine 中运行的函数;上下文对于多个 goroutine 同时使用是安全的。
2 context.Context引入
//上下文携带截止日期、取消信号和请求范围的值在API的界限。它的方法是安全的同时使用多个了goroutine。 type Context interface { // Done返回一个在上下文被取消或超时时关闭的通道。 Done()
- 该
Done
方法返回一个通道,该通道作为代表运行的函数的取消信号Context
:当通道关闭时,函数应该放弃它们的工作并返回。 - 该
Err
方法返回一个错误,指示Context
取消的原因。 - 一个
Context
对于多个 goroutine 同时使用是安全的。代码可以将单个传递Context
给任意数量的 goroutines 并取消它Context
以向所有goroutine 发出信号。 - 该
Deadline
方法允许函数确定它们是否应该开始工作,还可以使用截止日期来设置 I/O 操作的超时时间。 Value
允许一个Context
携带请求范围的数据。该数据必须是安全的,以便多个 goroutine 同时使用。
3 context包的其他常用函数
3.1 context.Background和context.TODO
Background是任何Context树的根,它永远不会被取消:
//Background返回一个空的Context。 它永远不会取消,没有截止日期,没有价值。 Background通常用于main、init和tests,并作为传入请求的顶级上下文。 func Background() Context
给一个函数方法传递Context的时候,不要传递nil,如果不知道传递什么,就使用context.TODO()
3.2 context.WithCancel和
WithCancelt返回派生的Context值,可以比父Context更快地取消。当请求处理程序返回时,通常会取消与传入请求关联的content。当使用多个副本时,WithCancel对于取消冗余请求也很有用。
// WithCancel返回一个父进程的副本,该父进程的Done通道被尽快关闭。 关闭Done或调用cancel。 func WithCancel(parent Context) (ctx Context, cancel CancelFunc) // CancelFunc取消一个上下文。 type CancelFunc func()
示例:
package main import ( "context" "fmt" ) func play(ctx context.Context) <p><code>扩展:go中select的用法</code></p> <pre class="brush:go;">``` select的用法与switch语言非常类似,由select开始一个新的选择块,每个选择条件由case语句来描述。 与switch语句相比, select有比较多的限制,其中最大的一条限制就是每个case语句里必须是一个IO操作,大致的结构如下: ``` go select { case <h3>3.3 context.WithTimeout</h3> <p>WithTimeout返回派生的Context值,WithTimeout用于设置请求到后端服务器的截止日期:</p> <pre class="brush:go;">//WithTimeout返回一个父进程的副本,该父进程的Done通道被立即关闭的父母。关闭“完成”、调用“取消”或超时结束。新 //Context的Deadline是现在的更快+timeout和父的Deadline,如果任何。 如果计时器仍然在运行,则cancel函数释放它资源。 func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) // CancelFunc取消一个上下文。 type CancelFunc func()
示例:
package main import ( "context" "fmt" "sync" "time" ) var wg sync.WaitGroup func worker(ctx context.Context) { LOOP: for { fmt.Println("db connecting ...") time.Sleep(time.Millisecond * 10) // 假设正常连接数据库耗时10毫秒 select { case <p><strong>执行结果:</strong></p> <p style="text-align:center"><img alt="" src="/uploads/20230101/167257355363b1727125bb0.png"></p> <h3>3.4 context.WithDeadline</h3> <pre class="brush:go;">func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) { if parent == nil { panic("cannot create context from nil parent") } if cur, ok := parent.Deadline(); ok && cur.Before(d) { // 目前的期限已经比新的期限提前 return WithCancel(parent) } c := &timerCtx{ cancelCtx: newCancelCtx(parent), deadline: d, } propagateCancel(parent, c) dur := time.Until(d) if dur <p><strong>示例:</strong></p> <pre class="brush:go;">package main import ( "context" "fmt" "time" ) func main() { d := time.Now().Add(500 * time.Millisecond) ctx, cancel := context.WithDeadline(context.Background(), d) // 尽管ctx会过期,但在任何情况下调用它的cancel函数都是很好的实践。 // 如果不这样做,可能会使上下文及其父类存活的时间超过必要的时间。 defer cancel() select { case <p><strong>执行结果:</strong></p> <p style="text-align:center"><img alt="" src="/uploads/20230101/167257355363b17271841c8.png"></p> <h3>3.5 context.WithValue</h3> <p><strong>WithValue提供了一种将请求范围的值与Context关联的方法 :</strong></p> <pre class="brush:go;">//WithValue返回父元素的副本,其Value方法返回val for key。 func WithValue(parent Context, key interface{}, val interface{}) Context
了解如何使用context包的最好方法是通过一个已工作的示例。
示例:
package main import ( "context" "fmt" "sync" "time" ) type TraceCode string var wg sync.WaitGroup func worker(ctx context.Context) { key := TraceCode("KEY_CODE") traceCode, ok := ctx.Value(key).(string) // 在子goroutine中获取trace code if !ok { fmt.Println("invalid trace code") } LOOP: for { fmt.Printf("worker,code:%s\n", traceCode) time.Sleep(time.Millisecond * 10) // 假设正常连接数据库耗时10毫秒 select { case <p><strong>执行结果:</strong></p> <p style="text-align:center"><img alt="" src="/uploads/20230101/167257355363b17271e2366.png"></p> <h2>4 实例:请求浏览器超时</h2> <p><strong>server端:</strong></p> <pre class="brush:go;">package main import ( "fmt" "math/rand" "net/http" "time" ) // server端,随机出现慢响应 func indexHandler(w http.ResponseWriter, r *http.Request) { number := rand.Intn(2) if number == 0 { time.Sleep(time.Second * 10) // 耗时10秒的慢响应 fmt.Fprintf(w, "slow response") return } fmt.Fprint(w, "quick response") } func main() { http.HandleFunc("/", indexHandler) err := http.ListenAndServe(":9999", nil) if err != nil { panic(err) } }
client端:
package main import ( "context" "fmt" "io/ioutil" "net/http" "sync" "time" ) // 客户端 type respData struct { resp *http.Response err error } func doCall(ctx context.Context) { // http长连接 transport := http.Transport{DisableKeepAlives: true} client := http.Client{Transport: &transport} respChan := make(chan *respData, 1) req, err := http.NewRequest("GET", "http://127.0.0.1:9999/", nil) if err != nil { fmt.Println(err) return } req = req.WithContext(ctx) // 使用带超时的ctx创建一个新的client request var wg sync.WaitGroup wg.Add(1) defer wg.Wait() go func() { resp, err := client.Do(req) fmt.Printf("resp:%v, err:%v\n", resp, err) rd := &respData{ resp: resp, err: err, } respChan <h2>5 Context包都在哪些地方使用</h2> <p>许多服务器框架提供了用于承载请求作用域值的包和类型。我们可以定义“Context”接口的新实现,在使用现有框架的代码和需要“Context”参数的代码之间架起桥梁。</p> <h2>6 小结</h2> <p>在谷歌中,要求Go程序员将“Context”参数作为传入和传出请求之间的调用路径上的每个函数的第一个参数传递。这使得许多不同团队开发的Go代码能够很好地互操作。它提供了对超时和取消的简单控制,并确保像安全凭证这样的关键值能够正确地传输Go程序。</p> <p>想要构建在“Context”上的服务器框架应该提供“Context”的实现来连接它们的包和那些需要“Context”参数的包。然后,它们的客户端库将接受来自调用代码的“Context”。通过为请求范围的数据和取消建立一个公共接口,“上下文”使包开发人员更容易共享创建可伸缩服务的代码。</p> <p>好了,本文到此结束,带大家了解了《一篇文章搞懂Go语言中的Context》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多Golang知识!</p>
版本声明
本文转载于:脚本之家 如有侵犯,请联系study_golang@163.com删除

- 上一篇
- go-micro开发RPC服务以及运行原理介绍

- 下一篇
- 一篇文章学会GO语言中的变量
评论列表
-
- 朴素的小懒猪
- 很好,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢师傅分享博文!
- 2023-06-20 00:16:00
-
- 整齐的白云
- 很详细,码住,感谢老哥的这篇文章,我会继续支持!
- 2023-04-25 07:57:47
-
- 柔弱的水杯
- 这篇技术贴太及时了,好细啊,很好,码起来,关注博主了!希望博主能多写Golang相关的文章。
- 2023-04-14 21:20:03
查看更多
最新文章
-
- Golang · Go教程 | 17小时前 |
- TigervncDebian多用户共享桌面超简单教程
- 482浏览 收藏
-
- Golang · Go教程 | 1天前 |
- Go语言新手必看!切片vs数组,一次搞定这两个核心知识点
- 472浏览 收藏
-
- Golang · Go教程 | 1天前 |
- Docker在Debian上运行超简单教程(保姆级教学)
- 210浏览 收藏
-
- Golang · Go教程 | 1天前 |
- Debian设置hostname踩坑记录:权限问题大揭秘
- 334浏览 收藏
-
- Golang · Go教程 | 1天前 |
- Debian装SQLServer?这些问题你一定要注意!
- 284浏览 收藏
-
- Golang · Go教程 | 2天前 |
- Debian系统下Jenkins自动化部署脚本教学
- 367浏览 收藏
-
- Golang · Go教程 | 2天前 |
- DebianSwap服务器应用实测,这些场景真的用得上!
- 319浏览 收藏
-
- Golang · Go教程 | 2天前 |
- Debian跑TigerVNC实测!真香警告,快来看看性能咋样~
- 171浏览 收藏
-
- Golang · Go教程 | 2天前 |
- 在Debian上玩转SQLServer备份还原,手把手教你一步步操作
- 498浏览 收藏
-
- Golang · Go教程 | 2天前 |
- DebianOverlay不会玩?手把手教你轻松定制化安装
- 258浏览 收藏
-
- Golang · Go教程 | 2天前 |
- Go语言实战:time.Ticker&time.After用法区别及避坑技巧
- 240浏览 收藏
-
- Golang · Go教程 | 2天前 |
- Debian系统如何快速定位&干掉那些讨厌的僵尸进程
- 317浏览 收藏
查看更多
课程推荐
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
查看更多
AI推荐
-
- 茅茅虫AIGC检测
- 茅茅虫AIGC检测,湖南茅茅虫科技有限公司倾力打造,运用NLP技术精准识别AI生成文本,提供论文、专著等学术文本的AIGC检测服务。支持多种格式,生成可视化报告,保障您的学术诚信和内容质量。
- 18次使用
-
- 赛林匹克平台(Challympics)
- 探索赛林匹克平台Challympics,一个聚焦人工智能、算力算法、量子计算等前沿技术的赛事聚合平台。连接产学研用,助力科技创新与产业升级。
- 50次使用
-
- 笔格AIPPT
- SEO 笔格AIPPT是135编辑器推出的AI智能PPT制作平台,依托DeepSeek大模型,实现智能大纲生成、一键PPT生成、AI文字优化、图像生成等功能。免费试用,提升PPT制作效率,适用于商务演示、教育培训等多种场景。
- 57次使用
-
- 稿定PPT
- 告别PPT制作难题!稿定PPT提供海量模板、AI智能生成、在线协作,助您轻松制作专业演示文稿。职场办公、教育学习、企业服务全覆盖,降本增效,释放创意!
- 53次使用
-
- Suno苏诺中文版
- 探索Suno苏诺中文版,一款颠覆传统音乐创作的AI平台。无需专业技能,轻松创作个性化音乐。智能词曲生成、风格迁移、海量音效,释放您的音乐灵感!
- 57次使用
查看更多
相关文章
-
- 优雅使用GoFrame共享变量Context示例详解
- 2023-01-18 401浏览
-
- Go语言上下文context底层原理
- 2023-01-22 175浏览
-
- golang的协程上下文的具体使用
- 2022-12-24 236浏览
-
- Go中groutine通信与context控制实例详解
- 2022-12-29 137浏览
-
- 对Go语言中的context包源码分析
- 2023-02-16 270浏览