Go語言map不支持并發(fā)寫操作的原因
本文介紹 Go 語言為什么不支持并發(fā)讀寫 map?,Go 官方的說法是在多數(shù)情況下 map 只存在并發(fā)讀操作,如果原生支持并發(fā)讀寫,即降低了并發(fā)讀操作的性能。在使用 map 時,要特別注意是否存在對 map 的并發(fā)寫操作,如果存在,要結合 sync 包的互斥鎖一起使用。
01 、介紹
在 Go 語言項目開發(fā)中,我們經(jīng)常會使用哈希表 map,它的時間復雜度是 O(1),Go 語言中的 map 使用開放尋址法避免哈希碰撞。
Go 語言中的 map 并非原子操作,不支持并發(fā)讀寫操作。
Go 官方認為 map 在大多數(shù)情況下是使用 map 進行并發(fā)讀操作,僅在少數(shù)情況下是使用 map 進行并發(fā)讀寫操作。
如果 Go 語言中的 map 原生支持并發(fā)讀寫操作,在操作時需要先獲取互斥鎖,反而會降低只有并發(fā)讀操作時的性能。
在需要并發(fā)讀寫操作 map 時,可以結合 sync 包中的互斥鎖一起使用。
02 、并發(fā)讀寫 map
Go 支持并發(fā)讀 map,不支持并發(fā)讀寫 map。
示例代碼:
func main() {
var m = make(map[int]string)
go func() {
for {
m[1] = "xx"
}
}()
go func() {
for {
_ = m[1]
}
}()
time.Sleep(time.Second * 3)
}輸出結果:
fatal error: concurrent map read and map write // ...
閱讀上面這段代碼,我們并發(fā)讀寫 map 類型的變量 m,在運行時,返回致命錯誤 fatal error: concurrent map read and map write。
Go 語言中的 map 在運行時是怎么檢測到 map 的存在寫操作?
源碼:
const (
// flags
iterator = 1 // there may be an iterator using buckets
oldIterator = 2 // there may be an iterator using oldbuckets
hashWriting = 4 // a goroutine is writing to the map
sameSizeGrow = 8 // the current map growth is to a new map of the same size
)
// A header for a Go map.
type hmap struct {
count int // # live cells == size of map. Must be first (used by len() builtin)
flags uint8
B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
hash0 uint32 // hash seed
buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated)
extra *mapextra // optional fields
}
// Like mapaccess, but allocates a slot for the key if it is not present in the map.
func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer {
// ...
done:
if h.flags&hashWriting == 0 {
fatal("concurrent map writes")
}
h.flags &^= hashWriting
if t.IndirectElem() {
elem = *((*unsafe.Pointer)(elem))
}
return elem
}閱讀上面這段源碼,我們可以發(fā)現(xiàn)在 hmap 結構體中的字段 flags,該字段用于標記 map 是否為寫入狀態(tài)。
在訪問 map 時,通過判斷 hmap.flags 和 hashWriting 的值,可知是否有其它 goroutine 訪問 map,如果有,則返回致命錯誤 fatal("concurrent map writes")。
03 、總結
本文介紹 Go 語言為什么不支持并發(fā)讀寫 map,Go 官方的說法是在多數(shù)情況下 map 只存在并發(fā)讀操作,如果原生支持并發(fā)讀寫,即降低了并發(fā)讀操作的性能。
通過閱讀源碼,我們了解到在運行時檢測是否存在其它 goroutine 對 map 的寫操作,如果存在,則返回致命錯誤。
讀者朋友們在使用 map 時,要特別注意是否存在對 map 的并發(fā)寫操作,如果存在,要結合 sync 包的互斥鎖一起使用。
到此這篇關于Go語言map不支持并發(fā)寫操作的原因的文章就介紹到這了,更多相關Go中map不支持并發(fā)寫內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Go語言普通指針unsafe.Pointer?uintpt之間的關系及指針運算
這篇文章主要為大家介紹了Go語言普通指針unsafe.Pointer?uintpt之間的關系及指針運算示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12
Golang中的new()和make()函數(shù)本質區(qū)別
在 Go 語言開發(fā)中,new() 和 make() 是兩個容易讓開發(fā)者感到困惑的內建函數(shù),盡管它們都用于內存分配,但其設計目的、適用場景和底層實現(xiàn)存在本質差異,本文將通過類型系統(tǒng)、內存模型和編譯器實現(xiàn)三個維度,深入解析這兩個函數(shù)的本質區(qū)別,感興趣的朋友一起看看吧2025-02-02
如何在golang中使用shopspring/decimal來處理精度問題
本文主要介紹了如何在golang中使用shopspring/decimal來處理精度問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-04-04
Go?語言?net/http?包使用之HTTP?服務器、客戶端與中間件詳解
Go 語言標準庫中的net/http包十分的優(yōu)秀,提供了非常完善的 HTTP 客戶端與服務端的實現(xiàn),僅通過幾行代碼就可以搭建一個非常簡單的 HTTP 服務器,本文給大家介紹Go語言net/http包使用之HTTP服務器、客戶端與中間件的操作,感興趣的朋友一起看看吧2025-05-05
Go通過goroutine實現(xiàn)多協(xié)程文件上傳的基本流程
多協(xié)程文件上傳是指利用多線程或多協(xié)程技術,同時上傳一個或多個文件,以提高上傳效率和速度,本文給大家介紹了Go通過goroutine實現(xiàn)多協(xié)程文件上傳的基本流程,需要的朋友可以參考下2024-05-05

