1. 程式人生 > >Go 中的 map 併發存取

Go 中的 map 併發存取

Catena (時序儲存引擎)中有一個函式的實現備受爭議,它從 map 中根據指定的 name 獲取一個 metricSource。每一次插入操作都會至少呼叫一次這個函式,現實場景中該函式呼叫更是頻繁,並且是跨多個協程的,因此我們必須要考慮同步。

該函式從 map[string]*metricSource 中根據指定的 name 獲取一個指向 metricSource 的指標,如果獲取不到則建立一個並返回。其中要注意的關鍵點是我們只會對這個 map 進行插入操作。

簡單實現如下:(為節省篇幅,省略了函式頭和返回,只貼重要部分)

var source *memorySource
var present bool

p.lock.Lock() // lock the mutex
defer p.lock.Unlock() // unlock the mutex at the end

if source, present = p.sources[name]; !present {
	// The source wasn't found, so we'll create it.
	source = &memorySource{
		name: name,
		metrics: map[string]*memoryMetric{},
	}

	// Insert the newly created *memorySource.
	p.sources[name] = source
}

經測試,該實現大約可以達到 1,400,000 插入/秒(通過協程併發呼叫,GOMAXPROCS 設定為 4)。看上去很快,但實際上它是慢於單個協程的,因為多個協程間存在鎖競爭。

我們簡化一下情況來說明這個問題,假設兩個協程分別要獲取“a”、“b”,並且“a”、“b”都已經存在於該 map 中。上述實現在執行時,一個協程獲取到鎖、拿指標、解鎖、繼續執行,此時另一個協程會被卡在獲取鎖。等待鎖釋放是非常耗時的,並且協程越多效能越差。

讓它變快的方法之一是移除鎖控制,並保證只有一個協程訪問這個 map。這個方法雖然簡單,但沒有伸縮性。下面我們看看另一種簡單的方法,並保證了執行緒安全和伸縮性。 

var source *memorySource
var present bool

if source, present = p.sources[name]; !present { // added this line
	// The source wasn't found, so we'll create it.

	p.lock.Lock() // lock the mutex
	defer p.lock.Unlock() // unlock at the end

	if source, present = p.sources[name]; !present {
		source = &memorySource{
			name: name,
			metrics: map[string]*memoryMetric{},
		}

		// Insert the newly created *memorySource.
		p.sources[name] = source
	}
	// if present is true, then another goroutine has already inserted
	// the element we want, and source is set to what we want.

} // added this line

// Note that if the source was present, we avoid the lock completely!

該實現可以達到 5,500,000 插入/秒,比第一個版本快 3.93 倍。有 4 個協程在跑測試,結果數值和預期是基本吻合的。

這個實現是 ok 的,因為我們沒有刪除、修改操作。在 CPU 快取中的指標地址我們可以安全使用,不過要注意的是我們還是需要加鎖。如果不加,某協程在建立插入 source 時另一個協程可能已經正在插入,它們會處於競爭狀態。這個版本中我們只是在很少情況下加鎖,所以效能提高了很多

相關推薦

Go map 併發存取

Catena (時序儲存引擎)中有一個函式的實現備受爭議,它從 map 中根據指定的 name 獲取一個 metricSource。每一次插入操作都會至少呼叫一次這個函式,現實場景中該函式呼叫更是頻繁,並且是跨多個協程的,因此我們必須要考慮同步。 該函式從 map[string]*metricSourc

Gomap的創建和初始化

切片 var 字典 默認 () project key 語義 操作 // code_014_map_usage project main.go package main import ( "fmt" ) func main() { /*Go語言中的map

Gomap的操作與使用(增刪改查)

map aaa 使用 () fun true 增刪改 println 值傳遞 // code_014_map_operator project main.go package main import ( "fmt" ) func DeleteMap(m map[

一段小程式淺析Go併發,協程(goroutine),sync.WaitGroup

package main import ( "fmt" "runtime" "sync" ) func main() { runtime.GOMAXPROCS(2) fmt.Println("begin typing") var wg sync.WaitGro

Go語言map併發操作

先單獨說一下map,在Go語言中,map與通道類似,都需要先用make函式進行初始化,之後才可以賦值,不過map沒有長度的限制,所以在使用make函式初始化map時,make函式的第二個引數不用寫,寫不寫沒有區別。 package main import "fmt" func mai

mapreducemap和reduce的最大併發數量設定

     reduce數量究竟多少是適合的。目前測試認為reduce數量約等於cluster中datanode的總cores的一半比較合適,比如cluster中有32臺datanode,每臺8 core,那麼reduce設定為128速度最快。因為每臺機器8 core,4個作m

go的資料結構字典-map

1. map的使用   golang中的map是一種資料型別,將鍵與值繫結到一起,底層是用雜湊表實現的,可以快速的通過鍵找到對應的值。   型別表示:map[keyType][valueType] key一定要是可比較的型別(可以理解為支援==的操作),value可以是任意型別。   初始化:map

GO常用包筆記 bytes(四)

g 學習筆記 bytes包Package bytes對字節數組進行操作的包。功能和strings包相似.bytes包提供的功能有:和另一個字節數組切片的關系(逐字節比較大小,是否相等/相似,是否包含/包含次數,位置搜索,是否是前綴後綴)2.字節數組切片和字符串的關系(字符串中是否含有字節數組所包含的rune,

Pythonmap和reduce函數

courier ref tail erl position 必須 第一個 http title ①從參數方面來講: map()函數: map()包含兩個參數,第一個是參數是一個函數,第二個是序列(列表或元組)。其中,函數(即map的第一個參數位置的函數)可以接收一個或多個參

python multiprocessing.Pool map、map_async、apply、apply_async的區別

pen pool arm res 區別 col apply rgs pytho   multiprocessing是python的多進程庫,multiprocessing.dummy則是多線程的版本,使用都一樣。   其中都有pool池的概念,進程池/線程池有共同的方法,其

Go的結構實現它的的寫法註意事項

pre 值傳遞 為什麽 div 錯誤 寫法 nta () clas 下面一個例子: type Student struct { name string age int } func (s Student) printAge() { fmt.Pr

JAVAMAP轉LIST

new pub values pac exc except gpo x11 toarray @Test public void testMap2List() throws Exception{ Map<String, String> map = new

mapreducemap數的測試

1.5 nbsp 啟動 小時 修改 cor core mar 並行 默認的map數是有邏輯的split的數量決定的,根據源碼切片大小的計算公式:Math.max(minSize, Math.min(maxSize, blockSize)); 其中: minsize:默認值:

JavaScriptMap和ForEach的區別

get 新的 create line 速度對比 圖片 技術分享 其中 原來 譯者按: 慣用Haskell的我更愛map。 原文: JavaScript?—?Map vs. ForEach - What’s the difference between Map and Fo

python3map()和reduce()函數的使用

ada lam map函數 fun name 元組 clas nor 列操作 問題一:利用map()函數,把用戶輸入的不規範的英文名字,變為首字母大寫,其他小寫的規範名字。輸入:[‘adam‘, ‘LISA‘, ‘barT‘],輸出:[‘Adam‘, ‘Lisa‘, ‘Ba

go語言---map

field 傳引用 塊存儲 === 聲明 print ann 維護 ava go語言---map https://blog.csdn.net/cyk2396/article/details/78890185 一.map的用法: type PersonDB struct {

js Map使用

遍歷 鍵值 是否 move text each val fun pty function Map() { /** 存放鍵的數組(遍歷用到) */ this.keys = new Array(); /** 存放數據 */

go的深度拷貝direct版

性能 player int() pen func 拷貝 sting 函數 mar 4.給每個結構寫拷貝函數(效率最高) package main import ( "fmt" "reflect" ) type ( Player struct { Id

GO 輸出打印的常用函數

字符串 常用函數 %d 輸出 fff nbsp erro 自動增加 不可 1.Println 可以打印字符串和變量(任何類型) println函數在輸出後自動增加一個換行 例: a:=10

go的make和new的區別

channel 有一點 區別 都是 chan new ron 術語 初始化 適用範圍:make 只能創建內建類型(slice map channel), new 則是可以對所有類型進行內存分配 返回值: new 返回指針, make 返回引用 填充值: new 填充零值,