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 會在...