共计 3401 个字符,预计需要花费 9 分钟才能阅读完成。
前言
这是 Go 十大常见谬误系列的第 8 篇:并发编程中 Context 应用常见谬误。素材来源于 Go 布道者,现 Docker 公司资深工程师 Teiva Harsanyi。
本文波及的源代码全副开源在:Go 十大常见谬误源代码,欢送大家关注公众号,及时获取本系列最新更新。
Context 是什么
Go 语言规范库里有一个 package 叫context
,该 package 里定义了 context.Context 类型,在并发编程里十分有用,然而也常常被开发者误会。
官网对 Context 的表述是:
Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes.
光看这段形容,还是很容易让人迷糊的,咱们接下来具体看看 Context 到底是什么以及能够帮忙咱们做什么事件。
Context 顾名思义,示意的是 goroutine 的上下文,Context 定义如下所示:
// A Context carries a deadline, cancellation signal, and request-scoped values
// across API boundaries. Its methods are safe for simultaneous use by multiple
// goroutines.
type Context interface {
// Done returns a channel that is closed when this Context is canceled
// or times out.
Done() <-chan struct{}
// Err indicates why this context was canceled, after the Done channel
// is closed.
Err() error
// Deadline returns the time when this Context will be canceled, if any.
Deadline() (deadline time.Time, ok bool)
// Value returns the value associated with key or nil if none.
Value(key interface{}) interface{}}
Context 能够通过超时设置、携带勾销信号、附加参数信息来不便 goroutine 里做相应的逻辑管制。
-
超时管制。通过
context.WithTimeout
函数和context.WithDeadline
函数能够创立一个有超时工夫的 Context。通过 Context 的Done
函数能够判断是否超时了。func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
-
勾销信号。通过
context.WithCancel
函数能够创立一个接管 cancel 信号的 Context。通过 Context 的Done
函数能够判断是否收回了 cancel 信号。父 Context 收回的 cancel 信号,子 Context 也能够接管到。func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
-
附加参数信息。通过
context.WithValue
函数能够给 Context 增加参数。其中 key 和 value 都是空接口类型 (interface{}
)。通过 Context 的Value
函数能够获取附加参数信息。func WithValue(parent Context, key, val any) Context
在理论开发过程中,Context 的应用流程个别是:
- Step 1: 创立 Context,给 Context 指定超时工夫,设置勾销信号,或者附加参数(链路跟踪里常常应用 Context 里的附加参数,传递 IP 等链路跟踪信息)。
-
Step 2: goroutine 应用 Step 1 里的 Context 作为第一个参数,在该 goroutine 里就能够做如下事件:
- 应用 Context 里的
Done
函数判断是否达到了 Context 设置的超时工夫或者 Context 是否被被动勾销了。 - 应用 Context 里的
Value
函数获取该 Context 里的附加参数信息。 - 应用 Context 里的
Err
函数获取谬误起因,目前起因就 2 个,要么是超时,要么是被动勾销。
- 应用 Context 里的
有 2 点要补充:
- 第一,Context 是能够组合的。比方,咱们能够通过
context.WithTimeout
创立一个有超时工夫的 Context,再调用context.WithValue
增加一些附加参数信息。 - 第二,多个 goroutine 能够共享同一个 Context,能够通过该 Context 的超时设置、携带勾销信号以及附加参数来管制多个 goroutine 的行为。
常见谬误
在 Context 应用过程中有以下几个常见谬误:
-
第一,不执行
cancel
函数去开释 Context 资源。-
对于
context.WithTimeout
、context.WithDeadline
、context.WithCancel
函数返回的 cancel 函数,须要做执行。官网阐明如下:Canceling this context releases resources associated with it, so code should call cancel as soon as the operations running in this Context complete:
参考代码示例:
func slowOperationWithTimeout(ctx context.Context) (Result, error) {ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond) defer cancel() // releases resources if slowOperation completes before timeout elapses return slowOperation(ctx) }
-
-
第二,不加超时管制,如果执行了十分耗时的 rpc 操作或者数据库操作,就会阻塞程序。如果 rpc 调用接口或者数据库操作接口反对传递 Context 参数,倡议加上超时设置。代码示例参考如下:
ctx, cancel := context.WithTimeout(parent, 100 * time.Millisecond) response, err := grpcClient.Send(ctx, request)
举荐浏览
- Go 十大常见谬误第 1 篇:未知枚举值
- Go 十大常见谬误第 2 篇:benchmark 性能测试的坑
- Go 十大常见谬误第 3 篇:go 指针的性能问题和内存逃逸
- Go 十大常见谬误第 4 篇:break 操作的注意事项
- Go 十大常见谬误第 5 篇:Go 语言 Error 治理
- Go 十大常见谬误第 6 篇:slice 初始化常犯的谬误
- Go 十大常见谬误第 7 篇:不应用 -race 选项做并发竞争检测
- Go 面试题系列,看看你会几题?
开源地址
文章和示例代码开源在 GitHub: Go 语言高级、中级和高级教程。
公众号:coding 进阶。关注公众号能够获取最新 Go 面试题和技术栈。
集体网站:Jincheng’s Blog。
知乎:无忌。
福利
我为大家整顿了一份后端开发学习材料礼包,蕴含编程语言入门到进阶常识(Go、C++、Python)、后端开发技术栈、面试题等。
关注公众号「coding 进阶」,发送音讯 backend 支付材料礼包,这份材料会不定期更新,退出我感觉有价值的材料。还能够发送音讯「进群」,和同行一起交流学习,答疑解惑。
References
- 参考文章:https://itnext.io/the-top-10-…
- 官网文档:https://pkg.go.dev/context
- 官网 Context 入门介绍:https://go.dev/blog/context
- Context 应用介绍:https://mp.weixin.qq.com/s/Po…
- https://www.digitalocean.com/…