Context 上下文
kevinyan815 opened this issue · 0 comments
Context 与 Go 语言中的并发编程有着比较密切的关系,在其他语言中我们很难见到类似 Context 的东西,它不仅能够用来设置截止日期、同步『信号』还能用来传递请求相关的值。
Go 语言里每一个并发的执行单元叫做 goroutine,当一个用Go语言编写的程序启动时,main 函数在一个单独的 goroutine 中运行。main 函数返回时,所有的goroutine都会被直接打断,程序退出。除此之外如果想通过编程的方法让一个goroutine 中断其他 goroutine 的执行,只能是通过在多个 goroutine 间用 context 上下文对象同步取消信号的方式来实现。
Context 其实是 Go 语言 context 包对外暴露的接口,
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
常用下面两个方法
-
Done 方法需要返回一个 Channel,多次调用 Done 方法会返回同一个 Channel,这个 Channel 会在工作完成或者上下文被取消之后close掉,通过关闭Channel的方式,实现通知使用该 Context 的所有goroutine。
-
Err 方法会返回当前 Context 结束的原因,它只在 Done 返回的 Channel 被关闭时才会返回非空的值;
如果当前 Context 被取消就会返回 Canceled 错误;
如果当前 Context 超时就会返回 DeadlineExceeded 错误;
Context一般有下面两种用法。
用法一:主动发起取消通知
使用context.WithCancel 创建一个支持取消功能的上下文。
func operation1(ctx context.Context) error {
// 假设这个操作会因为某种原因失败
// 使用time.Sleep来模拟一个资源密集型操作
time.Sleep(100 * time.Millisecond)
return errors.New("failed")
}
func operation2(ctx context.Context) {
for {
select {
case <-ctx.Done():
fmt.Println("canceled operation2")
return
// 注意:省略default分支 整个goroutine 会被阻塞住
case default :
}
// 可以选择在default分支或者是这里执行业务逻辑
time.Sleep(10 * time.Millisecond)
}
}
func main() {
// 新建一个上下文
ctx := context.Background()
// 在初始上下文的基础上创建一个有取消功能的上下文
ctx, cancel := context.WithCancel(ctx)
// 在不同的goroutine中运行operation2
go func() {
operation2(ctx)
}()
err := operation1(ctx)
// 如果这个操作返回错误,取消所有使用相同上下文的 goroutine 的执行
if err != nil {
cancel()
}
}
用法二:基于时间到期后自动取消通知
// 这个上下文将会在3秒后被取消
// 如果需要在到期前就取消可以像前面的例子那样使用cancel函数
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
// 上下文将在2009-11-10 23:00:00被取消
ctx, cancel := context.WithDeadline(ctx, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC))
worker goroutine 内部要跟上面的例子一样,通过 select case <- Done() 接收通道传递过来的取消信号。唯一的
区别是这两种Context是到期自动同步信号,不需要在 main goroutine 内主动触发。
如果想在到期前提前让 worker goroutine 结束执行,调用创建Context时返回的 cancel 函数。
更多 Context 的应用示例:https://github.com/kevinyan815/gocookbook/tree/master/codes/context_demo