日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

Go 学习笔记(67)— Go 并发安全字典 sync.Map

發布時間:2023/11/28 生活经验 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Go 学习笔记(67)— Go 并发安全字典 sync.Map 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 并發不安全的 map

Go 語言中的 map 在并發情況下,只讀是線程安全的,同時讀寫是線程不安全的。

換句話說,在同一時間段內,讓不同 goroutine 中的代碼,對同一個字典進行讀寫操作是不安全的。字典值本身可能會因這些操作而產生混亂,相關的程序也可能會因此發生不可預知的問題。

package mainimport ("fmt""time"
)func main() {m := map[int]string{1: "haha",}go read(m)time.Sleep(time.Second)go write(m)time.Sleep(30 * time.Second)fmt.Println(m)
}func read(m map[int]string) {for {_ = m[1]time.Sleep(1)}
}func write(m map[int]string) {for {m[1] = "write"time.Sleep(1)}
}

執行一段時間后會報錯:

fatal error: concurrent map read and map write

2. 并發安全字典 sync.Map

需要并發讀寫時,一般的做法是加鎖,但這樣性能并不高, Go 語言在 1.9 版本中提供了一種效率較高的并發安全的 sync.Mapsync.Mapmap 不同,不是以語言原生形態提供,而是在 sync 包下的特殊結構。

sync.Map 有以下特性:

  • 無須初始化,直接聲明即可。
  • sync.Map 不能使用 map 的方式進行取值和設置等操作,而是使用 sync.Map 的方法進行調用, Store 表示存儲, Load 表示獲取, Delete 表示刪除。
  • 使用 Range 配合一個回調函數進行遍歷操作,通過回調函數返回內部遍歷出來的值, Range 參數中回調函數的返回值在需要繼續迭代遍歷時,返回 true ,終止迭代遍歷時,返回 false
  • Store:存儲一對 key-value 值。
  • Load:根據 key 獲取對應的 value 值,并且可以判斷 key 是否存在。

  • LoadOrStore:如果 key 對應的 value 存在,則返回該 value;如果不存在,存儲相應的 value。

  • Delete:刪除一個 key-value 鍵值對。

  • Range:循環迭代 sync.Map,效果與 for range 一樣。

它所有的方法涉及的鍵和值的類型都是 interface{} ,也就是空接口,這意味著可以包羅萬象。所以,我們必須在程序中自行保證它的鍵類型和值類型的正確性。

并發安全的 sync.Map 演示代碼如下:

package mainimport ("fmt""sync"
)func main() {// 聲明 scene,類型為 sync.Map,注意,sync.Map 不能使用 make 創建。var scene sync.Map// 將鍵值對保存到sync.Map// sync.Map 將鍵和值以 interface{} 類型進行保存。scene.Store("greece", 97)scene.Store("london", 100)scene.Store("egypt", 200)// 從sync.Map中根據鍵取值fmt.Println(scene.Load("london"))// 根據鍵刪除對應的鍵值對scene.Delete("london")// 遍歷所有sync.Map中的鍵值對// 遍歷需要提供一個匿名函數,參數為 k、v,類型為 interface{},// 每次 Range() 在遍歷一個元素時,都會調用這個匿名函數把結果返回。scene.Range(func(k, v interface{}) bool {fmt.Println("iterate:", k, v)return true})}

輸出結果:

100 true
iterate: greece 97
iterate: egypt 200

sync.Map 鍵的實際類型不能是函數類型、字典類型和切片類型。由于這些鍵值的實際類型只有在程序運行期間才能夠確定,所以 Go 語言編譯器是無法在編譯期對它們進行檢查的,不正確的鍵值實際類型肯定會引發 panic

3. 如何保證并發安全字典中的鍵和值的類型正確性?

3.1 讓并發安全字典只能存儲某個特定類型的鍵。

比如指定這里的鍵只能是 int 類型的,或者只能是字符串,又或是某類結構體。一旦完全確定了鍵的類型,你就可以在進行存、取、刪操作的時候,使用類型斷言表達式去對鍵的類型做檢查了。

一般情況下,這種檢查并不繁瑣。而且,你要是把并發安全字典封裝在一個結構體類型里面,那就更加方便了。你這時完全可以讓 Go 語言編譯器幫助你做類型檢查。

package mainimport ("fmt""sync"
)// IntStrMap 代表鍵類型為int、值類型為string的并發安全字典。
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)
}// pairs 代表測試用的鍵值對列表。
var pairs = []struct {k intv string
}{{k: 1, v: "a"},{k: 2, v: "b"},{k: 3, v: "c"},{k: 4, v: "d"},
}func main() {var iMap IntStrMapiMap.Store(pairs[0].k, pairs[0].v)iMap.Store(pairs[1].k, pairs[1].v)iMap.Store(pairs[2].k, pairs[2].v)fmt.Println("[Three pairs have been stored in the IntStrMap instance]")iMap.Range(func(key int, value string) bool {fmt.Printf("The result of an iteration in Range: %d, %s\n", key, value)return true})k0 := pairs[0].kv0, ok := iMap.Load(k0)fmt.Printf("The result of Load: %v, %v (key: %v)\n", v0, ok, k0)k3 := pairs[3].kv3, ok := iMap.Load(k3)fmt.Printf("The result of Load: %v, %v (key: %v)\n", v3, ok, k3)k2, v2 := pairs[2].k, pairs[2].vactual2, loaded2 := iMap.LoadOrStore(k2, v2)fmt.Printf("The result of LoadOrStore: %v, %v (key: %v, value: %v)\n",actual2, loaded2, k2, v2)v3 = pairs[3].vactual3, loaded3 := iMap.LoadOrStore(k3, v3)fmt.Printf("The result of LoadOrStore: %v, %v (key: %v, value: %v)\n",actual3, loaded3, k3, v3)k1 := pairs[1].kiMap.Delete(k1)fmt.Printf("[The pair with the key of %v has been removed from the IntStrMap instance]\n", k1)v1, ok := iMap.Load(k1)fmt.Printf("The result of Load: %v, %v (key: %v)\n", v1, ok, k1)v1 = pairs[1].vactual1, loaded1 := iMap.LoadOrStore(k1, v1)fmt.Printf("The result of LoadOrStore: %v, %v (key: %v, value: %v)\n",actual1, loaded1, k1, v1)iMap.Range(func(key int, value string) bool {fmt.Printf("The result of an iteration in Range: %d, %s\n", key, value)return true})fmt.Println()
}

sync.Map 沒有提供獲取 map 數量的方法,替代方法是在獲取 sync.Map 時遍歷自行計算數量。

sync.Map 為了保證并發安全有一些性能損失,因此在非并發情況下,使用 map 相比使用 sync.Map 會有更好的性能。

總結

以上是生活随笔為你收集整理的Go 学习笔记(67)— Go 并发安全字典 sync.Map的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。