context主要的用处如果用一句话来说,是在于控制goroutine的生命周期。当一个计算任务被goroutine承接了之后,由于某种原因(超时,或者强制退出)我们希望中止这个goroutine的计算任务,那么就用得到这个Context了。
Context 的调用应该是链式的,通过WithCancel,WithDeadline,WithTimeout或WithValue派生出新的 Context。当父 Context 被取消时,其派生的所有 Context 都将取消。
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
Context
接口包含四个方法:
Deadline
返回绑定当前context
的任务被取消的截止时间;如果没有设定期限,将返回ok == false
。Done
当绑定当前context
的任务被取消时,将返回一个关闭的channel
;如果当前context
不会被取消,将返回nil
。Err
如果Done
返回的channel
没有关闭,将返回nil
;如果Done
返回的channel
已经关闭,将返回非空的值表示任务结束的原因。如果是context
被取消,Err
将返回Canceled
;如果是context
超时,Err
将返回DeadlineExceeded
。Value
返回context
存储的键值对中当前key
对应的值,如果没有对应的key
,则返回nil
。
可以看到Done
方法返回的channel
正是用来传递结束信号以抢占并中断当前任务;Deadline
方法指示一段时间后当前goroutine
是否会被取消;以及一个Err
方法,来解释goroutine
被取消的原因;而Value
则用于获取特定于当前任务树的额外信息。而context
所包含的额外信息键值对是如何存储的呢?其实可以想象一颗树,树的每个节点可能携带一组键值对,如果当前节点上无法找到key
所对应的值,就会向上去父节点里找,直到根节点。