Go十大常见错误第8篇(并发编程中Context使用常见错误)
前言
这是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
- Step 1: 创建Context,给Context指定超时时间,设置取消信号,或者附加参数(链路跟踪里经常使用Context里的附加参数,传递IP等链路跟踪信息)。
- Step 2: goroutine使用Step 1里的Context作为第一个参数,在该goroutine里就可以做如下事情:
- 使用Context里的
Done
函数判断是否达到了Context设置的超时时间或者Context是否被主动取消了。 - 使用Context里的
Value
函数获取该Context里的附加参数信息。 - 使用Context里的
Err
函数获取错误原因,目前原因就2个,要么是超时,要么是主动取消。
- 使用Context里的
- 第一,Context是可以组合的。比如,我们可以通过
context.WithTimeout
创建一个有超时时间的Context,再调用context.WithValue
添加一些附加参数信息。 - 第二,多个goroutine可以共享同一个Context,可以通过该Context的超时设置、携带取消信号以及附加参数来控制多个goroutine的行为。
- 第一,不执行
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面试题系列,看看你会几题?
公众号:coding进阶。关注公众号可以获取最新Go面试题和技术栈。
个人网站:Jincheng's Blog。
知乎:无忌。
福利 我为大家整理了一份后端开发学习资料礼包,包含编程语言入门到进阶知识(Go、C++、Python)、后端开发技术栈、面试题等。
【Go十大常见错误第8篇(并发编程中Context使用常见错误)】关注公众号「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/...
推荐阅读
- 排序算法整理C++(初赛)
- go语言学习(3)--分支和循环
- 安装cocopods时出现错误
- 解决gitlab|解决gitlab runner在docker模式下报 503 Service Unavailable 错误
- 废掉小脑阔的错误|上采样、下采样区别及作用
- Linux软件包常见的几种下载、安装方法
- 10大高考招生常见骗局,快看你中招了没(-翻转课堂)
- 学习问题|mysql操作中 出现You can‘t specify target table for update in FROM clause错误的解决方法
- 总结这一年玩微信的十大心得
- 面对错误