Go 標準庫 context 原始碼解析

2021-10-14 08:09:02 字數 4187 閱讀 4826

context 主要用來在goroutine 之間傳遞上下文資訊,包括取消訊號、超時時間、截止時間、k-v等。標準庫的許多介面加上了 context 引數,來實現併發控制和超時控制。

type context inte***ce 

err() error

value(key inte***ce{}) inte***ce{}

}

該介面定義了4個方法,分別是:

type canceler inte***ce 

}

該介面將 context 與取消操作分離開 

type emptyctx int

func (*emptyctx) deadline() (deadline time.time, ok bool)

func (*emptyctx) done() <-chan struct{}

func (*emptyctx) err() error

func (*emptyctx) value(key inte***ce{}) inte***ce{}

給出的乙個預設實現,該context不會被取消、沒有截止時間、沒有存鍵值對,它被包裝成兩個結構,並提供匯出函式對外公開

var (

background = new(emptyctx)

todo = new(emptyctx)

)func background() context

func todo() context

可以取消的乙個 context

type cancelctx struct 

children map[canceler]struct{}

err error

}

建立乙個cancelctx

func withcancel(parent context) (ctx context, cancel cancelfunc) 

c := newcancelctx(parent)

propagatecancel(parent, &c)

return &c, func()

}// 建立乙個新的可取消的 context 並返回

func newcancelctx(parent context) cancelctx

}// 該方法的作用是向上尋找乙個可掛靠的、可取消的 context,並將當前 context 掛靠上去,當上層 cancel 方法呼叫時,可層層傳遞使得掛靠的子 context 也同時取消

func propagatecancel(parent context, child canceler)

select

if p, ok := parentcancelctx(parent); ok else )

}p.children[child] = struct{}{}

} p.mu.unlock()

} else

}() }}

// 查詢可取消的父 context 並返回

func parentcancelctx(parent context) (*cancelctx, bool)

p, ok := parent.value(&cancelctxkey).(*cancelctx)

if !ok

p.mu.lock()

ok = p.done == done

p.mu.unlock()

if !ok

return p, true

}

實現了value、done、err方法:

func (c *cancelctx) value(key inte***ce{}) inte***ce{} 

return c.context.value(key)

}func (c *cancelctx) done() <-chan struct{} )

} d := c.done

c.mu.unlock()

return d

}func (c *cancelctx) err() error

還實現了 canceler 的 cancel 方法:

// removefromparent: 是否需要將掛靠在父 context 的子 context 移除

func (c *cancelctx) cancel(removefromparent bool, err error)

c.mu.lock()

if c.err != nil

c.err = err

// 關閉 channel,通知其他協程

if c.done == nil else

for child := range c.children

c.children = nil

c.mu.unlock()

// 從父節點移除自己

if removefromparent

}func removechild(parent context, child canceler)

p.mu.lock()

if p.children != nil

p.mu.unlock()

}

可以設定截止時間的乙個 context(基於cancelctx)

type timerctx struct
建立乙個timerctx:

// 實際呼叫 withdeadline 方法,傳的deadline是當前時間加上 timeout 的時間

func withtimeout(parent context, timeout time.duration) (context, cancelfunc)

func withdeadline(parent context, d time.time) (context, cancelfunc)

if cur, ok := parent.deadline(); ok && cur.before(d)

c := &timerctx

propagatecancel(parent, c)

// 當前距離 deadline 的時間

dur := time.until(d)

if dur <= 0

} c.mu.lock()

defer c.mu.unlock()

if c.err == nil )

} return c, func()

}

實現了 context 的 deadline 方法:

// 返回超時時間

func (c *timerctx) deadline() (deadline time.time, ok bool)

實現了 canceler 的 cancel 方法: 

func (c *timerctx) cancel(removefromparent bool, err error) 

c.mu.lock()

if c.timer != nil

c.mu.unlock()

}

可以傳值的乙個 context

type valuectx struct 

}

建立乙個 valuectx:

func withvalue(parent context, key, val inte***ce{}) context 

if key == nil

if !reflectlite.typeof(key).comparable()

return &valuectx

}

實現了 value 方法:

func (c *valuectx) value(key inte***ce{}) inte***ce{} 

// 取值時是乙個遞迴查詢過程,若當前 context 沒有則向其父 context 進行查詢

return c.context.value(key)

}

Go標準庫plugin原始碼分析 動態庫使用

go通常都是編譯打包成乙個可執行檔案,好處是不需要考慮依賴庫,乙個可執行檔案,拷貝就可以直接執行。但還有些場景,如需要以外掛程式形式載入,方便動態更新,熱重啟等情況。這些場景的需求,就帶來了plugin庫,動態庫載入。那麼先看看plguin這個庫的文件。文件為1.10版本 編譯命令和普通的編譯不一樣...

Go 原始碼學習 log 庫

log 包實現了乙個簡單的日誌功能。logger 結構體作為日誌物件,生成文字行到 io.writer。每次記錄日誌的操作都生成一行日誌,即便是log.printf也不用包含 n。結構體中有sync.metex鎖,保證了在多 goroutine 情況時的順序寫入。type logger struct...

golang 原始碼分析之context

context 上下文 context.context 是用來設定截止日期 同步訊號,傳遞請求相關值的結構體。該介面定義了四個需要實現的方法 deadline 返回 context.context 被取消的時間,也就是完成工作的截止日期 done 返回乙個 channel,這個 channel 會在...