1. 程式人生 > 其它 >golang之 sync.Map

golang之 sync.Map

今天併發操作map報錯了,查詢資料尋找到解決方法

一map不是併發安全的

當有多個併發的groutine讀寫同一個map時會出現panic錯誤

二 解決方式

  • 使用channel
  • map加鎖
  • sync.map

三 例項

// 建立一個int到int的對映
m := make(map[int]int)

// 開啟一段併發程式碼
go func() {

    // 不停地對map進行寫入
    for {
        m[1] = 1
    }

}()

// 開啟一段併發程式碼
go func() {

    // 不停地對map進行讀取
    for {
        _ = m[1]
    }

}()

// 無限迴圈, 讓併發程式在後臺執行
for { }
fatal error: concurrent map read and map write

sync.Map 有以下特性:

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

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

package main

import (
      "fmt"
      "sync"
)

func main() {

    var scene sync.Map

    // 將鍵值對儲存到sync.Map
    scene.Store("greece", 97)
    scene.Store("london", 100)
    scene.Store("egypt", 200)

    // 從sync.Map中根據鍵取值
    fmt.Println(scene.Load("london"))

    // 根據鍵刪除對應的鍵值對
    scene.Delete("london
") // 遍歷所有sync.Map中的鍵值對 scene.Range(func(k, v interface{}) bool { fmt.Println("iterate:", k, v) return true }) }
100 true
iterate: egypt 200
iterate: greece 97

程式碼說明如下:

  • 第 10 行,宣告 scene,型別為 sync.Map,注意,sync.Map 不能使用 make 建立。
  • 第 13~15 行,將一系列鍵值對儲存到 sync.Map 中,sync.Map 將鍵和值以 interface{} 型別進行儲存。
  • 第 18 行,提供一個 sync.Map 的鍵給 scene.Load() 方法後將查詢到鍵對應的值返回。
  • 第 21 行,sync.Map 的 Delete 可以使用指定的鍵將對應的鍵值對刪除。
  • 第 24 行,Range() 方法可以遍歷 sync.Map,遍歷需要提供一個匿名函式,引數為 k、v,型別為 interface{},每次 Range() 在遍歷一個元素時,都會呼叫這個匿名函式把結果返回。


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

  本文參考(Go語言sync.Map(在併發環境中使用的map) (biancheng.net)