请问怎么使用 context.Context package里面的 Done()
context.Context是一个接口,它定义了四个方法:
-
Deadline() (deadline time.Time, ok bool)
-
Done() <-chan struct{}
-
Err() error
-
Value(key interface{}) interface{}
Done()方法返回一个chan struct{},我们可以在这个channel上等待context被cancel的信号。这通常用于协程间通信,告诉其他协程这个context已经结束。举个例子:
ctx, cancel := context.WithCancel(context.Background())
go func() {
for {
select {
case <-ctx.Done():
println("context cancelled")
return
default:
// do some work
}
}
}()
// cancel context after 1 second
time.AfterFunc(1*time.Second, cancel)
在这个例子中,我们启动一个协程开始循环做一些工作。同时这个协程也会 select监听ctx.Done() channel。 过了1秒后,我们调用cancel()方法取消context。这时候ctx.Done() channel就会被关闭,协程里的select会检测到并退出循环。
所以Done() channel给我们提供了一个在context结束时通知其他 interessiated 方的机制。我们可以用它来构建复杂的上下文和取消机制。
select 如果没有满足条件, 会阻塞运行吗
在Go的select语句中,如果没有任何case分支满足条件,select语句会阻塞。例如:
select {} // 永远阻塞
这个select语句永远也不会有分支被选中,所以会一直阻塞。但是,如果其中至少有一个case的通信可以进行,select就不会阻塞:
chan1 := make(chan int)
chan2 := make(chan int)
select {
case <-chan1:
println("chan1")
case <-chan2:
println("chan2")
}
这里至少有chan1和chan2两个case可以进行通信,所以select不会阻塞,而是随机选择一个可执行的case执行。select的阻塞特性常用来实现超时效果:
select {
case <-ch:
// ch可读,执行case
case <-time.After(1 * time.Second):
// 1秒后超时,执行此case
}
这里如果ch在1秒内 readable,select会选中第一个case;如果1秒后ch还是不可读的,select会选中第二个case执行,实现超时效果。
所以总结来说:
- 如果select中没有任何一个case的通信可以进行,它会一直阻塞
- 如果select中至少有一个case的通信可以进行,它就会随机执行一个可进行通信的case
- 这些特性常用于实现channel的超时效果