前言
哈嘍,大家好,我是asong
。最近一個群裡看到一個有趣的八股文,問題是:使用context
攜帶的value
是執行緒安全的嗎?這道題其實就是考察面試者對context
實現原理的理解,如果不知道context
的實現原理,很容易答錯這道題,所以本文我們就藉著這道題,再重新理解一遍context
攜帶value
的實現原理。
context
攜帶value
是執行緒安全的嗎?
先說答案,context
本身就是執行緒安全的,所以context
攜帶value
也是執行緒安全的,寫個簡單例子驗證一下:
func main() {
ctx := context.WithValue(context.Background(), "asong", "test01")
go func() {
for {
_ = context.WithValue(ctx, "asong", "test02")
}
}()
go func() {
for {
_ = context.WithValue(ctx, "asong", "test03")
}
}()
go func() {
for {
fmt.Println(ctx.Value("asong"))
}
}()
go func() {
for {
fmt.Println(ctx.Value("asong"))
}
}()
time.Sleep(10 * time.Second)
}
程式正常執行,沒有任何問題。
但是context
對攜帶的資料沒有型別限制,所以任何資料型別都是用context
攜帶,在攜帶的資料型別是指標型別時,就不是執行緒安全的,來看一個例子:
func main() {
m := make(map[string]string)
m ["asong"] = "Golang夢工廠"
ctx := context.WithValue(context.Background(), "asong", m)
go func() {
for {
m1 := ctx.Value("asong")
mm := m1.(map[string]string)
mm["asong"] = "123213"
}
}()
go func() {
for {
m1 := ctx.Value("asong")
mm := m1.(map[string]string)
mm["asong"] = "123213"
}
}()
time.Sleep(10 * time.Second)
}
執行結果:
fatal error: concurrent map writes
goroutine 18 [running]:
runtime.throw({0x1072af2, 0x0})
......
為什麼執行緒安全?
context
包提供兩種建立根context
的方式:
context.Backgroud()
context.TODO()
又提供了四個函式基於父Context
衍生,其中使用WithValue
函式來衍生context
並攜帶資料,每次呼叫WithValue
函式都會基於當前context
衍生一個新的子context
,WithValue
內部主要就是呼叫valueCtx
類:
func WithValue(parent Context, key, val interface{}) Context {
if parent == nil {
panic("cannot create context from nil parent")
}
if key == nil {
panic("nil key")
}
if !reflectlite.TypeOf(key).Comparable() {
panic("key is not comparable")
}
return &valueCtx{parent, key, val}
}
valueCtx
結構如下:
type valueCtx struct {
Context
key, val interface{}
}
valueCtx
繼承父Context
,這種是採用匿名介面的繼承實現方式,key,val
用來儲存攜帶的鍵值對。
通過上面的程式碼分析,可以看到新增鍵值對不是在原context
結構體上直接新增,而是以此context
作為父節點,重新建立一個新的valueCtx
子節點,將鍵值對新增在子節點上,由此形成一條context
鏈。
獲取鍵值過程也是層層向上呼叫直到最終的根節點,中間要是找到了key
就會返回,否會就會找到最終的emptyCtx
返回nil
。
畫個圖表示一下:
總結:context
新增的鍵值對一個鏈式的,會不斷衍生新的context
,所以context
本身是不可變的,因此是執行緒安全的,但是如果我們攜帶的資料是指標型別,這時依然有執行緒不安全的風險。
總結
本文主要是想帶大家回顧一下context
的實現原理,面試中面試官都喜歡隱晦提出問題,所以這就需要我們有很紮實的基本功,一不小心就會掉入面試官的陷阱,要處處小心哦~
好啦,本文到這裡就結束了,我是asong,我們下期見。
歡迎關注公眾號:【Golang夢工廠】