Golang 在 1.6.2 的时候还没有自己的 context,在1.7的版本中就把 http://golang.org/x/net/context包被加入到了官方的库中。Golang 的Context 包,中文可以称之为“上下文”,是用来在 goroutine 协程之间进行上下文信息传递的,这些上下文信息包括 kv 数据、取消信号、超时时间、截止时间等。
Context 接口含义
Context 是以接口的形式对外提供的,其接口定义如下:
type Context interface {
// 返回此上下文的截止时间(如果有)。
// 如果没有截止时间,则ok为false。
Deadline() (deadline time.Time, ok bool)
// 返回与此上下文关联的取消函数。
Done() <-chan struct{}
// 返回 done() 的原因
Err() error
// 返回此上下文的键值对数据。
Value(key interface{}) interface{}
}
Context 接口提供四个方法:
Deadline()
方法——返回 context 的超时截止日期,如果没有设置超时截止日期,则返回false
。当时间达到超时截止日期时,context 会自动被取消
Done()
方法——返回一个只读的 channel,当 context 被取消或者超时截止日期到达时,该 channel 会被关闭。当接收到该 channel 关闭的信号时,就意味着该 context 被取消。Err()
方法——当Done信道关闭后,如果是被取消掉的,那么这里返回 Canceled 错误;如果是超时了,那么这里返回 DeadlineExceeded 错误。Value()
方法——用于在 context 中存储和获取键值对数据。该方法是非线程安全的。
Context 的类型
Go 语言中常用的 Context 类型有以下几种
context.Background()
Background context 是 Context 接口的一个默认实现,它没有任何值,也不会被取消。当没有更合适的 context 实例时,可以使用 background context。context.TODO()
TODO context 是 Context 接口的一个默认实现,它和 background context 类似,但是它是一个标记未完成工作的 context,用于暂时占位,待后续替换为真正的 context 实例。context.WithCancel(parent)
WithCancel 函数可以派生一个子 context,同时返回一个取消函数,用于在需要的时候取消该 context。当父 context 被取消或者取消函数被调用时,子 context 也会被取消。context.WithDeadline(parent, deadline)
WithDeadline 函数可以派生一个子 context,同时返回一个取消函数,用于在需要的时候取消该 context。与 WithCancel 不同的是,WithDeadline 可以设置一个超时截止日期,当截止日期到达时,子 context 会自动被取消。context.WithTimeout(parent, timeout)
WithTimeout 函数是 WithDeadline 的一个特例,它也可以派生一个子 context,并设置超时时间。与 WithDeadline 不同的是,WithTimeout 可以设置一个相对于超时任务,使用 WithTimeout 更为常见。context.WithValue(parent, key, val)
WithValue 函数可以派生一个子 context,并在其中存储键值对数据。该方法不是线程安全的,因此在并发环境下使用时需要注意。
Context 的使用
在使用 Context 时,需要遵循以下最佳实践:
- 在函数参数中添加一个 context 参数,以便于 Goroutine 可以获取到该 context。
- 如果一个 Goroutine 创建了多个子 Goroutine,那么应该将相同的 context 实例传递给所有子 Goroutine。
- 当一个 context 被取消时,它派生的所有子 context 也应该被取消。
- 当一个 context 被取消时,其关联的资源(如数据库连接、文件描述符等)应该被释放。
- 当使用 WithDeadline 和 WithTimeout 时,应该考虑到超时时间是否合理,过短的超时时间会导致任务失败,过长的超时时间会浪费资源。