golang使用context作為goroutine之間的控制器,舉例:
<code>package main import ( "context" "log" "time" ) func UseContext(ctx context.Context) { for { select { case /<code>
首先看下context是個什麼東東
context包裡面是一個interface接口,實現了下面4個方法
<code>type Context interface { Deadline() (deadline time.Time, ok bool) // Deadline() 返回上下文完成工作的時間 Done() /<code>
既然是接口,就找一個具體實現
<code>// background返回的是一個實現了 Context接口全部方法 的空方法的context func Background() Context { return background } // but what is background? it's: var ( background = new(emptyCtx) todo = new(emptyCtx) ) type emptyCtx int // 下面是實現context接口的4個方法 func (*emptyCtx) Deadline() (deadline time.Time, ok bool) { return } func (*emptyCtx) Done() /<code>
Background()既然是一個實現了ctx接口全部方法的空實例,那麼context.WithCancel()是一個什麼東東
<code>// 主實現邏輯,傳入一個context,返回一個context和一個CancelFunc(取消函數) func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { c := newCancelCtx(parent) // 初始化一個取消函數 propagateCancel(parent, &c) return &c, func() { c.cancel(true, Canceled) } } // 初始化一個cancelCtx,context等於傳進來的 func newCancelCtx(parent Context) cancelCtx { return cancelCtx{Context: parent} } type cancelCtx struct { Context mu sync.Mutex // 空值 done chan struct{} // 空值 children map[canceler]struct{} // 空值 err error // 空值 }/<code>
上面可以看出done其實是一個空結構體的chan,接下來看是如何把信號傳遞給done這個chan的,下面看下cancel()(取消函數)
<code>// 取消關閉 c.done,消去c的每個子結點, // 如果 removeFromParent為真,從父類的子類中移除c。 func (c *cancelCtx) cancel(removeFromParent bool, err error) { if err == nil { panic("context: internal error: missing cancel error") } c.mu.Lock() if c.err != nil { c.mu.Unlock() return // already canceled } c.err = err if c.done == nil { c.done = closedchan } else { close(c.done) // 調用c.done } for child := range c.children { // NOTE: acquiring the child's lock while holding parent's lock. child.cancel(false, err) } c.children = nil c.mu.Unlock() if removeFromParent { removeChild(c.Context, c) } }/<code>
這裡調用cancel的時候就是調用了 close(c.done)