go sync.map的使用


前言

數據競爭是並發情況下,存在多線程/協程讀寫相同數據的情況,必須存在至少一方寫。另外,全是讀的情況下是不存在數據競爭的。

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

如果map由多協程同時讀和寫就會出現 fatal error:concurrent map read and map write的錯誤。這是因為map在Go語言並發編程中,如果僅用於讀取數據時候是安全的,但是在讀寫操作的時候是不安全的,在Go語言1.9版本后提供了一種並發安全的,sync.Map是Go語言提供的內置map,不同於基本的map數據類型,所以不能像操作基本map那樣的方式操作數據,他提供了特有的方法,不需要初始化操作實現增刪改查的操作。

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

sync.Map 特性

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

sync.Map提供的常用方法有如下七個:

  • Load(key interface{}) (value interface{},ok bool):通過參數key查詢對應的value,如果不存在則返回nil;ok表示是否找到對應的值。
  • Store(key,value interface{}):該方法相當於對sync.Map的更新或新增,參數是鍵值對。
  • LoadOrStore(key,value interface{}) (actual interface{},loaded bool):該方法的參數為key和value。該方法會先根據參數key查找對應的value,如果找到則不修改原來的值並通過actual返回,並且loaded為true;如果通過key無法查找到對應的value,則存儲key-value並且將存儲的value通過actual返回,loaded為false。
  • Delete(key interface{}):通過key刪除鍵值對。
  • LoadAndDelete(key interface{}):通過key刪除鍵的值,如果有,則返回上一個值。
  • Range(f func(key,value interface{}) bool):遍歷sync.Map的元素,注意for...range map是對內置map類型的用法,sync.Map需要使用單獨的Range方法。

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

package main

import (
    "fmt"
    "sync"
)

//聲明sync.Map
var syncmap sync.Map

func main() {

    //Store方法將鍵值對保存到sync.Map
    syncmap.Store("zhangsan", 97)
    syncmap.Store("lisi", 100)
    syncmap.Store("wangmazi", 200)

    // LoadOrStore key不存在
    v, ok := syncmap.LoadOrStore(3, "three")
    fmt.Println(v, ok) // three false
    // LoadOrStore key存在
    v, ok = syncmap.LoadOrStore(1, "thisOne")
    fmt.Println(v, ok) // one ture

    // Load方法獲取sync.Map 鍵所對應的值
    fmt.Println(syncmap.Load("lisi"))

    // Delete方法鍵刪除對應的鍵值對
    syncmap.Delete("lisi")

    var syncmap sync.Map
    // LoadAndDelete key不存在
    v, ok := syncmap.LoadAndDelete("xiaomi")
    fmt.Println(v, ok) // <nil> false
    syncmap.Store("xiaomi", "xiaomi")
    // LoadAndDelete key存在
    v, ok = syncmap.LoadAndDelete("xiaomi")
    fmt.Println(v, ok) // xiaomi true

    // Range遍歷所有sync.Map中的鍵值對
    syncmap.Range(func(k, v interface{}) bool {
        fmt.Println(k, v)
        return true
    })

}

注意

聲明 score,類型為 sync.Map,注意,sync.Map 不能使用 make 創建。

將一系列鍵值對保存到 sync.Map 中,sync.Map 將鍵和值以 interface{} 類型進行保存。

Range() 方法可以遍歷 sync.Map,遍歷需要提供一個匿名函數,參數為 k、v,類型為 interface{},每次 Range() 在遍歷一個元素時,都會調用這個匿名函數把結果返回。

sync.Map 沒有提供獲取 map 數量的方法,替代方法是在獲取 sync.Map 時遍歷自行計算數量,sync.Map 為了保證並發安全有一些性能損失,因此在非並發情況下,使用 map 相比使用 sync.Map 會有更好的性能。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM