個人博客:https://www.luozhiyun.com/archives/215java
Context類型能夠提供一類表明上下文的值。此類值是併發安全的,也就是說它能夠被傳播給多個 goroutine。node
Context類型的值(如下簡稱Context值)是能夠繁衍的,這意味着咱們能夠經過一個Context值產生出任意個子值。這些子值能夠攜帶其父值的屬性和數據,也能夠響應咱們經過其父值傳達的信號。安全
context包中還包含了四個用於繁衍Context值的函數,即:WithCancel、WithDeadline、WithTimeout和WithValue。數據結構
全部的Context值共同構成了一顆表明了上下文全貌的樹形結構。經過調用context.Background函數就能夠獲得上下文根節點,而後經過根節點能夠產生子節點。以下:併發
rootNode := context.Background() node1, cancelFunc1 := context.WithCancel(rootNode)
在上面的例子中,初始化了一個撤銷節點,這個節點是能夠給它全部子節點發送撤銷信號的,以下:函數
cxt, cancelFunc := context.WithCancel(context.Background()) //發送撤銷信號 cancelFunc() //接受撤銷信號 <-cxt.Done()
在撤銷函數被調用以後,對應的Context值會先關閉它內部的接收通道,也就是它的Done方法會返回的那個通道。code
而後,它會向它的全部子值(或者說子節點)傳達撤銷信號。這些子值會如法炮製,把撤銷信號繼續傳播下去。最後,這個Context值會斷開它與其父值之間的關聯。
對象
WithValue函數在產生新的Context值(如下簡稱含數據的Context值)的時候須要三個參數,即:父值、鍵和值。blog
在咱們調用含數據的Context值的Value方法時,它會先判斷給定的鍵,是否與當前值中存儲的鍵相等,若是相等就把該值中存儲的值直接返回,不然就到其父值中繼續查找。接口
如:
node2 := context.WithValue(node1, 20, values[0]) node3 := context.WithValue(node2, 30, values[1]) fmt.Printf("The value of the key %v found in the node3: %v\n", keys[0], node3.Value(keys[0])) fmt.Printf("The value of the key %v found in the node3: %v\n", keys[1], node3.Value(keys[1])) fmt.Printf("The value of the key %v found in the node3: %v\n", keys[2], node3.Value(keys[2])) fmt.Println()
最後,提醒一下,Context接口並無提供改變數據的方法。
sync.Pool類型只有兩個方法——Put和Get。Put 用於在當前的池中存放臨時對象,它接受一個interface{}類型的參數;Get方法可能會從當前的池中刪除掉任何一個值,而後把這個值做爲結果返回。若是沒有那麼會使用當前池的New字段建立一個新值,並直接將其返回。
以下:
var ppFree = sync.Pool{ New: func() interface{} { return new(pp) }, }
Go 語言運行時系統中的垃圾回收器,因此在每次開始執行以前,都會對全部已建立的臨時對象池中的值進行全面地清除。
在臨時對象池中,有一個多層的數據結構。這個數據結構的頂層,咱們能夠稱之爲本地池列表。
在本地池列表中的每一個本地池都包含了三個字段(或者說組件),它們是:存儲私有臨時對象的字段private、表明了共享臨時對象列表的字段shared,以及一個sync.Mutex類型的嵌入字段。
臨時對象池的Put方法總會先試圖把新的臨時對象,存儲到對應的本地池的private字段中,只有當這個private字段已經存有某個值時,該方法纔會去訪問本地池的shared字段。
Put方法會在互斥鎖的保護下,把新的臨時對象追加到共享臨時對象列表的末尾。
臨時對象池的Get方法,總會先試圖從對應的本地池的private字段處獲取一個臨時對象。只有當這個private字段的值爲nil時,它纔會去訪問本地池的shared字段。
Get方法也會在互斥鎖的保護下,試圖把該共享臨時對象列表中的最後一個元素值取出並做爲結果。
鍵的實際類型不能是函數類型、字典類型和切片類型。因爲這些鍵值的實際類型只有在程序運行期間纔可以肯定,因此 Go 語言編譯器是沒法在編譯期對它們進行檢查的,不正確的鍵值實際類型確定會引起 panic。
也是由於Go沒有相似java的泛型,因此咱們一般要本身作類型限制,以下:
type IntStrMap struct { m sync.Map } func (iMap *IntStrMap) Delete(key int) { iMap.m.Delete(key) } func (iMap *IntStrMap) Load(key int) (value string, ok bool) { v, ok := iMap.m.Load(key) if v != nil { value = v.(string) } return } func (iMap *IntStrMap) LoadOrStore(key int, value string) (actual string, loaded bool) { a, loaded := iMap.m.LoadOrStore(key, value) actual = a.(string) return } func (iMap *IntStrMap) Range(f func(key int, value string) bool) { f1 := func(key, value interface{}) bool { return f(key.(int), value.(string)) } iMap.m.Range(f1) } func (iMap *IntStrMap) Store(key int, value string) { iMap.m.Store(key, value) }
在IntStrMap類型的方法簽名中,明確了鍵的類型爲int,且值的類型爲string。這些方法在接受鍵和值的時候,就不用再作類型檢查了。
或者能夠用反射來作類型校驗,以下:
type ConcurrentMap struct { m sync.Map keyType reflect.Type valueType reflect.Type } func NewConcurrentMap(keyType, valueType reflect.Type) (*ConcurrentMap, error) { if keyType == nil { return nil, errors.New("nil key type") } if !keyType.Comparable() { return nil, fmt.Errorf("incomparable key type: %s", keyType) } if valueType == nil { return nil, errors.New("nil value type") } cMap := &ConcurrentMap{ keyType: keyType, valueType: valueType, } return cMap, nil } func (cMap *ConcurrentMap) Load(key interface{}) (value interface{}, ok bool) { if reflect.TypeOf(key) != cMap.keyType { return } return cMap.m.Load(key) } func (cMap *ConcurrentMap) Store(key, value interface{}) { if reflect.TypeOf(key) != cMap.keyType { panic(fmt.Errorf("wrong key type: %v", reflect.TypeOf(key))) } if reflect.TypeOf(value) != cMap.valueType { panic(fmt.Errorf("wrong value type: %v", reflect.TypeOf(value))) } cMap.m.Store(key, value) }