Go并发模式:Context
介绍
在Go服务器中,每个传入请求都在自己的goroutine中处理。请求处理程序通常启动额外的goroutine来访问后台,如数据库和RPC服务。处理请求的goroutine集合通常需要访问特定于请求的值,例如用户身份标识、授权令牌和请求的超时时间等。当一个请求被取消或请求超时时,所有处理该请求的goroutine都应该迅速退出,这样系统就可以回收它们正在使用的资源。《Go Concurrency Patterns: Context》
Context
context文包的核心是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{}
}
- Done 方法返回一个 Channel 对象。在 Context 被取消时,这个channel 会被关闭。cancel、timeout、deadline 都可能导致Done被关闭。Done被关闭的时候,可以通过 ctx.Err() 获取错误信息。
- Deadline 方法会返回这个 Context 被取消的截止日期。如果没有设置则第二个返回值ok是 false。
- Value 返回此 ctx 中和指定的 key 对应的 value。
context 包中有两个常用的方法:
- context.Background() 一般会用来创建根Context;该方法返回一个空Context;没有值。
- context.TODO() 如果不清楚该用哪个Context,或不知道要传递一些什么上下文信息的时候可以使用
Context使用规约
- 1、函数使用 Context作为参数的时候会把context作为第一个参数。
- 2、不要使用 nil 当做 Context 类型的参数值,可以使用 context.Background()
- 3、不要将上下文存储在结构类型内;而是将Context显式的透传给每个需要它的函数。
- 4、key 的类型不应该是字符串类型或者其它内建类型。使用WithValue时,key 的类型应该是自己定义的类型或者struct{}。
- 5、使用WithCancel、WithDeadline、WithTimeout或WithValue替换上下文。
- 6、取消上下文时,从中派生的所有上下文也将被取消。
举个例子
使用 Context 来取消一个 goroutine 的运行,goroutine 需要尝试检查 Context 的 Done 是否关闭
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
go func() {
defer func() {
fmt.Println("this goroutine exit !!!")
}()
for {
select {
case <-ctx.Done():
return
default:
fmt.Println("do something")
time.Sleep(time.Second)
}
}
}()
time.Sleep(3 * time.Second)
cancel()
time.Sleep(2 * time.Second)
}