Go語言sync.Map詳解及使用場景
在 Go 語言中,sync.Map
是一個(gè)并發(fā)安全的映射結(jié)構(gòu),專門用于在高并發(fā)場景下處理鍵值對數(shù)據(jù)。它的并發(fā)安全是通過內(nèi)部實(shí)現(xiàn)機(jī)制來保證的,而不是依賴外部的鎖機(jī)制(如 sync.Mutex
或 sync.RWMutex
)來手動保護(hù)操作。
sync.Map
并發(fā)安全的實(shí)現(xiàn)原理
sync.Map
采用了一種更復(fù)雜的數(shù)據(jù)結(jié)構(gòu)和操作策略來實(shí)現(xiàn)并發(fā)安全。它的核心設(shè)計(jì)可以分為以下幾個(gè)方面:
1. 讀寫分離機(jī)制
sync.Map
的內(nèi)部結(jié)構(gòu)是通過讀寫分離實(shí)現(xiàn)的,主要由兩個(gè)部分組成:
- 只讀部分(read map):用于存儲穩(wěn)定的數(shù)據(jù)。讀取操作主要從這個(gè)只讀部分進(jìn)行,避免鎖的使用。
- 臟數(shù)據(jù)部分(dirty map):當(dāng)數(shù)據(jù)發(fā)生修改(寫入、刪除)時(shí),會被移動到臟數(shù)據(jù)區(qū)域,寫入的同時(shí)加鎖來確保并發(fā)安全。
2. 快速讀取路徑
- 無鎖讀取:如果數(shù)據(jù)已經(jīng)存在于
read map
中(即穩(wěn)定的數(shù)據(jù)),讀取操作不需要加鎖,這使得sync.Map
的讀操作非常高效。 - 寫時(shí)復(fù)制:當(dāng)數(shù)據(jù)在
read map
中不存在時(shí),可能存在于dirty map
中。此時(shí)需要升級鎖并從dirty map
讀取或?qū)懭霐?shù)據(jù)。
3. 寫入時(shí)的鎖保護(hù)
- 當(dāng)需要寫入(
Store
或Delete
)時(shí),sync.Map
會在dirty map
中進(jìn)行操作。寫操作會加鎖,以確保并發(fā)寫入時(shí)的安全性。 - 每次寫入時(shí),
sync.Map
都會檢查read map
和dirty map
之間的數(shù)據(jù)是否需要同步(比如數(shù)據(jù)量超過某個(gè)閾值時(shí)),并對臟數(shù)據(jù)部分進(jìn)行清理和遷移。
4. 懶惰同步(Lazy Synchronization)
當(dāng)讀操作頻繁時(shí),sync.Map
會把部分臟數(shù)據(jù)逐步遷移到 read map
,從而減少讀操作對鎖的依賴。這種延遲同步策略保證了讀操作可以盡量避免鎖競爭,從而提升讀取性能。
5. 原子操作
sync.Map
的部分操作(如 LoadOrStore
、LoadAndDelete
等)采用了原子操作。它們的實(shí)現(xiàn)使用了底層的原子性檢查和賦值操作,確保這些操作能夠在并發(fā)環(huán)境中保持一致性。
關(guān)鍵操作說明
讀操作 (
Load
):- 首先從
read map
中讀取,如果找到,直接返回。 - 如果在
read map
中沒有找到,則會嘗試從dirty map
中讀取,同時(shí)可能會觸發(fā)一次鎖定操作。
- 首先從
寫操作 (
Store
):- 寫操作會鎖定
sync.Map
,以保證在并發(fā)環(huán)境下對dirty map
的安全寫入。 - 如果臟數(shù)據(jù)變多或?qū)懭腩l繁,可能會觸發(fā)
read map
的同步,將一些臟數(shù)據(jù)遷移到read map
。
- 寫操作會鎖定
刪除操作 (
Delete
):- 刪除操作也會加鎖,并刪除
dirty map
中的數(shù)據(jù)。
- 刪除操作也會加鎖,并刪除
批量操作 (
Range
):Range
操作遍歷sync.Map
中的所有數(shù)據(jù),確保在遍歷期間不會發(fā)生并發(fā)沖突。
代碼示例
package main import ( "fmt" "sync" ) func main() { var m sync.Map // 寫入數(shù)據(jù) m.Store("foo", 42) m.Store("bar", 100) // 讀取數(shù)據(jù) value, ok := m.Load("foo") if ok { fmt.Println("foo:", value) } // 刪除數(shù)據(jù) m.Delete("foo") // 使用 Range 遍歷所有元素 m.Range(func(key, value interface{}) bool { fmt.Println(key, value) return true }) }
sync.Map
的優(yōu)點(diǎn)
- 讀性能高:在讀多寫少的場景下表現(xiàn)非常優(yōu)異,因?yàn)?nbsp;
read map
讀取時(shí)不需要加鎖,減少了鎖競爭。 - 自動并發(fā)控制:
sync.Map
不需要手動管理鎖機(jī)制,減少了編寫并發(fā)安全代碼的復(fù)雜度。 - 適合高并發(fā)場景:特別是在大量讀取的情況下,
sync.Map
的性能優(yōu)于傳統(tǒng)的map
+sync.RWMutex
的方案。
何時(shí)使用 sync.Map
- 讀多寫少的場景:當(dāng)并發(fā)訪問主要是讀操作,寫操作較少時(shí),
sync.Map
的讀寫分離機(jī)制使得它具有很高的性能。 - 需要簡單并發(fā)訪問:當(dāng)需要并發(fā)訪問
map
,而且不想手動管理鎖時(shí),sync.Map
是一個(gè)非常方便的工具。
何時(shí)不使用 sync.Map
- 寫操作非常頻繁:
sync.Map
在寫操作上需要加鎖,如果寫操作占比很高,可能不如手動加鎖的傳統(tǒng)map
方案效率高。
到此這篇關(guān)于Go語言sync.Map詳解及使用場景的文章就介紹到這了,更多相關(guān)Go語言sync.Map內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
golang validator參數(shù)校驗(yàn)的實(shí)現(xiàn)
這篇文章主要介紹了golang validator參數(shù)校驗(yàn)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10golang中set數(shù)據(jù)結(jié)構(gòu)的使用示例
本文主要介紹了golang中set數(shù)據(jù)結(jié)構(gòu)的使用示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03基于Go實(shí)現(xiàn)TCP長連接上的請求數(shù)控制
在服務(wù)端開啟長連接的情況下,四層負(fù)載均衡轉(zhuǎn)發(fā)請求時(shí),會出現(xiàn)服務(wù)端收到的請求qps不均勻的情況或是服務(wù)器無法接受到請求,因此需要服務(wù)端定期主動斷開一些長連接,所以本文給大家介紹了基于Go實(shí)現(xiàn)TCP長連接上的請求數(shù)控制,需要的朋友可以參考下2024-05-05淺談Go語言不提供隱式數(shù)字轉(zhuǎn)換的原因
本文主要介紹了淺談Go語言不提供隱式數(shù)字轉(zhuǎn)換的原因,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03linux中用shell快速安裝配置Go語言的開發(fā)環(huán)境
相信每位開發(fā)者都知道選擇一門開發(fā)語言,免不了需要安裝配置開發(fā)環(huán)境,所以這篇文章給大家分享了linux下使用shell一鍵安裝配置GO語言開發(fā)環(huán)境的方法,有需要的朋友們可以參考借鑒,下面來一起看看吧。2016-10-10GO中?分組聲明與array,?slice,?map函數(shù)
這篇文章主要介紹了GO中?分組聲明與array,slice,map函數(shù),Go語言中,同時(shí)聲明多個(gè)常量、變量,或者導(dǎo)入多個(gè)包時(shí),可采用分組的方式進(jìn)行聲明,下面詳細(xì)介紹需要的小伙伴可以參考一下2022-03-03一文帶你掌握Go語言并發(fā)模式中的Context的上下文管理
在?Go?的日常開發(fā)中,Context?上下文對象無處不在,無論是處理網(wǎng)絡(luò)請求、數(shù)據(jù)庫操作還是調(diào)用?RPC?等場景,那你真的熟悉它的正確用法嗎,隨著本文一探究竟吧2023-05-05