context 很重要,總體作用就是 設(shè)置程序執(zhí)行的
- deadline 設(shè)置程序的截止日期
- status sync 同步信號(hào)
- submit value 設(shè)置共享變量
當(dāng)上層 goroutine 需要通知 他下級(jí) goroutine 及時(shí)終止程序,釋放資源的時(shí)候,常用 context
簡(jiǎn)單來說,這三個(gè)功能貌似都可以使用 channel 實(shí)現(xiàn),比如所有 gorountine 實(shí)現(xiàn)關(guān)閉,只要判斷 channel close 了,就把所有函數(shù) return ,釋放資源。傳遞值也可以 通過 channel, 但是 記住 channel 的東西拿到一次就清除了。而context value 卻可以多次取值。還有很多區(qū)別,下面就詳細(xì)說一下 context 都有那些用處。
參考 [go 設(shè)計(jì)和實(shí)現(xiàn) 上下文 ](Context https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-context/)
context.Context 里面有什么東西
type Context interface {
Deadline() (deadline time.Time, ok bool) // 設(shè)置截至日期
Done() <-chan struct{} // 當(dāng)前上下文 被取消時(shí)候,關(guān)閉,
Err() error // 上下文結(jié)束的時(shí)候,返回 Canceled / DeadlineExceeded 錯(cuò)誤
Value(key interface{}) interface{} // 獲取鍵值對(duì)
}
他就是一個(gè)接口,很簡(jiǎn)單吧,這個(gè)接口只是定義了最基本的 context 的東西,很多context 都是實(shí)現(xiàn)了這個(gè)接口,然后擴(kuò)展了很多功能。
context.Context 里面的幾個(gè)函數(shù)
context.emptyCtx
這個(gè)可以看出 她是 context 包的私有屬性, 他只是為了 提供一個(gè) 實(shí)現(xiàn)了 context.Context 接口的 空 上下文,因?yàn)樗鼘?shí)現(xiàn)的函數(shù),沒有具體的功能,只是為了給別人繼承用的
type emptyCtx int
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
func (*emptyCtx) Err() error {
return nil
}
func (*emptyCtx) Value(key interface{}) interface{} {
return nil
}
context.Background , context.TODO
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
func Background() Context {
return background
}
func TODO() Context {
return todo
}
其實(shí) Background 和 TODO 實(shí)質(zhì)試一樣的,只是 變量名有種表面的含義
Background 代表 默認(rèn)上下文, 而 TODO 代表 可能還不確定,怎么用。只是給開發(fā)人員看起來知道大概 context 大概用來干嘛的
(ps context 是可以被嵌套的,一級(jí)一級(jí) 包裝, 添加更多的功能,就像繼承一樣)
創(chuàng)建一個(gè)可以被取消的 context
context.WithCancel
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
// 在 其他 函數(shù)使用 ctx
cancel( ) // 關(guān)閉所有 goroutine 相關(guān)
// 所有使用 ctx 的 goroutine ctx.Done() 會(huì)有值, 那就表示要結(jié)束了,我們可以用 for select 結(jié)構(gòu) <- ctx.Done() 這樣判斷是不是需要結(jié)束這個(gè) gorountine。
for {
select {
case <- ctx.Done():
child.cancel(false, parent.Err()) // 父上下文已經(jīng)被取消
return
default:
// todo
}
創(chuàng)建一個(gè)可以被 定時(shí) 關(guān)閉的 context
context.WithDeadline // 第二個(gè)參數(shù)是時(shí)間對(duì)象
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
context.WithTimeout // 第二個(gè)參數(shù)是時(shí)間間隔
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return WithDeadline(parent, time.Now().Add(timeout))
}
ctx, cancel := context.WithDeadline( context.Background(), time.Now().Add(3 * time.Second) )
// 三秒之后自動(dòng)調(diào)用 cancel 函數(shù), 當(dāng)然你也可以手動(dòng)提前執(zhí)行 cancel
// 其實(shí) WithDeadline 內(nèi)部是 調(diào)用 time.AfterFunc 延遲調(diào)用的 cancel
創(chuàng)建一個(gè) 可以傳值的 context
context.WithValue 部分代碼
type valueCtx struct {
Context
key, val interface{}
}
func WithValue(parent Context, key, val interface{}) Context {
if key == nil {
panic("nil key")
}
if !reflectlite.TypeOf(key).Comparable() {
panic("key is not comparable")
}
return &valueCtx{parent, key, val}
}
func (c *valueCtx) Value(key interface{}) interface{} {
if c.key == key {
return c.val
}
return c.Context.Value(key)
}
繼承一個(gè)父 context 和一個(gè) key value 值,就是簡(jiǎn)單的 設(shè)置 key value 的值, 取值 用 ctx.Value(key) 當(dāng)前 context 沒有 key 就去 上層 去找。