GoLand map中的并發問題——為什么會造成并發問題?該怎么解決?

GoLand map中的并發問題——為什么會造成并發問題?該怎么解決?

  • 問題提出
  • 原因解析
    • 具體原因
    • 競態檢測器
  • 如何解決并發問題呢?
    • 方法一 : 使用sync.Mutex
    • 方法二: 使用sync.Map
      • 我們首先了解一下sync.Map的常用方法:
        • Store(key, value interface{})
        • Load(key interface{}) (value interface{}, ok bool)
        • LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)
        • Delete(key interface{})
        • Range(f func(key, value interface{}) bool)
      • 修改之前的代碼
  • 總結
  • 注釋 : 競態檢測器

問題提出

大家在使用map的時候,一定遇到過一個問題,由于map并不是線程安全,所以就會導致并發問題的出現。

下面先給大家演示一下這個問題:

func main() {m := make(map[string]int, 2)m["dd"] = 22go func() {for {m["ff"] = 1}}()go func() {for {_ = m["dd"]}}()time.Sleep(1 * time.Hour)
}

會出現以下報錯:

fatal error: concurrent map read and map write

為什么會拋出這個錯誤呢?


原因解析

具體原因

這個錯誤其實是“故意”設計給map的,在map的底層代碼里寫好的,為了避免map出現并發問題,用來確保數據的正確性的。

 if h.flags&hashWriting != 0 {fatal("concurrent map read and map write")}

map這么設計有兩點好處:

  1. 保證了map的運行性能,不使用鎖機制降低了程序運行的開銷
  2. 避免了map運行時造成不可預期的錯誤。比如map的漸進式擴容,在沒有并發的情況下,開啟擴容的前提一定是沒有處于擴容狀態,才能讓每一步操作分擔運行成本;如果并發操作,沒有辦法保證在下一次擴容之前完成了前一次的漸進擴容。

競態檢測器

不過大家可能會發現,map源碼中是有一個競態檢測器的代碼

	// 如果啟用了競態檢測并且h不為nil,進行競態檢測。if raceenabled && h != nil {callerpc := getcallerpc()pc := abi.FuncPCABIInternal(mapaccess1)racereadpc(unsafe.Pointer(h), callerpc, pc)raceReadObjectPC(t.Key, key, callerpc, pc)}

這個玩意干什么的呢?為啥有了這個東西還是不能避免并發問題呢?

  1. 這個東西在平時的生產環境是默認關閉的,開啟需要在執行前輸入"-race",像下面這樣(如果運行有問題,見文末注釋
go run -race main.go
  1. 這個東西只能用來檢測并發程序中的競態條件,并不能規避并發問題!

例如上面舉例的并發錯誤代碼,用-race運行結果是這樣的

D:\GoLand 2024.1.1\program\test
go run -race main.go
==================
WARNING: DATA RACE
Write at 0x00c000020060 by goroutine 6:runtime.mapassign_faststr()D:/GoLand 2024.1.1/Go/src/runtime/map_faststr.go:203 +0x0main.main.func1()D:/GoLand 2024.1.1/program/test/main.go:12 +0x44Previous read at 0x00c000020060 by goroutine 7:runtime.mapaccess1_faststr()D:/GoLand 2024.1.1/Go/src/runtime/map_faststr.go:13 +0x0main.main.func2()D:/GoLand 2024.1.1/program/test/main.go:17 +0x44Goroutine 6 (running) created at:main.main()D:/GoLand 2024.1.1/program/test/main.go:10 +0xc5Goroutine 7 (running) created at:main.main()D:/GoLand 2024.1.1/program/test/main.go:15 +0x130
==================
fatal error: concurrent map read and map writegoroutine 6 [running]:
main.main.func2()D:/GoLand 2024.1.1/program/test/main.go:17 +0x45
created by main.main in goroutine 1D:/GoLand 2024.1.1/program/test/main.go:15 +0x131goroutine 1 [sleep]:
time.Sleep(0x34630b8a000)D:/GoLand 2024.1.1/Go/src/runtime/time.go:195 +0x126
main.main()D:/GoLand 2024.1.1/program/test/main.go:20 +0x145goroutine 5 [runnable]:
main.main.func1()D:/GoLand 2024.1.1/program/test/main.go:12 +0x45
created by main.main in goroutine 1D:/GoLand 2024.1.1/program/test/main.go:10 +0xc6
exit status 2

WARNING: DATA RACE 就意味著發生了并發問題,還有并發問題的詳細信息


如何解決并發問題呢?

有兩種常用方法

方法一 : 使用sync.Mutex

我們可以使用互斥鎖(sync.Mutex)來保護map的并發訪問。在寫入或讀取map之前,我們需要獲取鎖,以確保同一時間只有一個goroutine可以訪問map。

type SafeMap struct {mu sync.Mutexm  map[string]int
}func NewSafeMap() *SafeMap {return &SafeMap{m: make(map[string]int),}
}func (sm *SafeMap) Set(key string, value int) {sm.mu.Lock()defer sm.mu.Unlock()sm.m[key] = value
}func (sm *SafeMap) Get(key string) (int, bool) {sm.mu.Lock()defer sm.mu.Unlock()val, ok := sm.m[key]return val, ok
}func main() {m := NewSafeMap()m.Set("dd", 22)go func() {for {m.Set("ff", 1)}}()go func() {for {_, _ = m.Get("dd")}}()time.Sleep(1 * time.Hour)
}

方法二: 使用sync.Map

我們首先了解一下sync.Map的常用方法:

  • Store(key, value interface{})

用于添加或更新鍵值對。如果鍵已存在,它的值將被新值覆蓋。

var m sync.Map
m.Store("exampleKey", "exampleValue")
  • Load(key interface{}) (value interface{}, ok bool)

用于獲取鍵對應的值。如果鍵存在,返回鍵對應的值和true;如果不存在,返回nil和false。

if value, ok := m.Load("exampleKey"); ok {fmt.Println("Value found:", value)
}
  • LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)

嘗試從映射中加載鍵的值。如果鍵不存在,它將存儲鍵值對到映射中。返回加載到的值(或存儲的值)和一個布爾值,表示值是否被加載。

if actual, loaded := m.LoadOrStore("exampleKey", "newValue"); loaded {fmt.Println("Value loaded:", actual)
} else {fmt.Println("Value stored:", actual)
}
  • Delete(key interface{})

用于刪除映射中的鍵及其對應的值。

m.Delete("exampleKey")
  • Range(f func(key, value interface{}) bool)

用于迭代映射中的所有鍵值對。它接受一個函數作為參數,該函數會被調用每個鍵值對。如果該函數返回false,迭代將停止。

m.Range(func(key, value interface{}) bool {fmt.Println("Key:", key, "Value:", value)return true // 繼續迭代
})

修改之前的代碼

func main() {var m sync.Mapm.Store("dd", 22)go func() {for {m.Store("ff", 1)}}()go func() {for {_, _ = m.Load("dd")}}()time.Sleep(1 * time.Hour)
}

總結

  • 為了保證性能,將map設置成了不可以并發
  • 想要并發操作map,可以使用sync.Mutex 或者sync.Map

注釋 : 競態檢測器

  1. 大家在使用"-race"啟動的時候,可能會遇到下面的問題:
go: -race requires cgo; enable cgo by setting CGO_ENABLED=1翻譯:Go: -race要求Go;通過設置CGO_ENABLED=1開啟cgo

解決方法 —— 使用env -w 修改環境變量的值:

go env -w CGO_ENABLED=1
  1. 之后可能還會出現下面的錯誤
cgo: C compiler "gcc" not found: exec: "gcc": executable file not found in %PATH%

先把gcc安裝一下,配置一下環境變量就可以了~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/14890.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/14890.shtml
英文地址,請注明出處:http://en.pswp.cn/web/14890.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

2024.5.24.python.exercise

# python文件操作 # f open("打字版.txt", "a", encoding"UTF-8") # writer input("請輸入你想要寫入到文件的內容") # f.write(writer) # f.flush() # f.close() # f open("打字版.txt", "r", encoding"…

代碼隨想錄算法訓練營第三十九天 | 738.單調遞增的數字、968.監控二叉樹 (可以跳過)

監控二叉樹同樣的等代碼隨想錄刷完后,再回頭來看,先跳過 738.單調遞增的數字 代碼隨想錄 解題思路 例如:98,一旦出現strNum[i - 1] > strNum[i]的情況(非單調遞增),首先想讓strNum[i - 1]--…

游戲引擎支持腳本編程的好處

哈嘍呀,大家好,淼淼又來和大家見面啦,咱們今天來聊聊游戲引擎,游戲引擎作為現代游戲開發的核心,它集成了圖形渲染、物理模擬、音頻處理、動畫系統、輸入輸出控制等多種復雜技術于一體,為開發者提供了一個高…

ASP+ACCESS基于WEB網上留言板

摘要 本文概述了ACCESS數據庫及其相關的一些知識,著重論述ACCESS數據庫和ASP的中間技術,構建一個簡單的留言板。具體的實現是構造一個留言板系統,能很方便的和同學溝通和交流。留言板具有功能強大、使用方便的特點。用戶以個人的身份進入&am…

瑞芯微RV1126——人臉識別源碼分析

本節內容主要分為3部分,第一部分是流程結構圖;第二部分為人臉識別代碼流程;第三部分為具體的代碼分析。 1.流程結構圖 2.人臉識別代碼流程 1、人臉數據的初始化: init_all_rockx_face_data();init_face_data();2、創建rtsp會話,這里包括發…

一個典型的分布式緩存系統是什么樣的?no.32

分布式 Redis 服務 由于本課程聚焦于緩存,接下來,我將以微博內的 分布式 Redis 服務系統為例,介紹一個典型的分布式緩存系統的組成。 微博的 Redis 服務內部也稱為 RedisService。RedisService 的整體架構如圖所示。主要分為Proxy、存儲、集…

產品推薦 | 基于Xilinx XCKU115的半高PCIe x8 硬件加速卡

一、板卡概述 本板卡系我公司自主研發,采用Xilinx公司的XCKU115-3-FLVF1924-E芯片作為主處理器,主要用于FPGA硬件加速。板卡設計滿足工業級要求。如下圖所示: 二、功能和技術指標 板卡功能 參數內容 主處理器 XCKU115-3-FLVF1924-E 板卡…

UE4/UE5像素流送云推流:多人訪問不穩定、畫面糊、端口占用多等

UE4/UE5想要實現網頁訪問,很多工程師會選擇guan方的像素流送。但這個技術要求在模型開發初期就接入。對于一些已有UE模型是無法進行流化的。雖然也可以解決新UE模型的網頁訪問問題,但在實際的應用中,點量云流也收到很多反饋說,使用…

netty-socketio 集群隨記

實現netty-socketio集群的方式 代碼實例 PostConstructpublic void subscribe() {pubSubStore.subscribe(PubSubType.DISPATCH, new PubSubListener<DispatchMessage>() {Overridepublic void onMessage(DispatchMessage message) {log.debug("subscribe: {}"…

Python爬取B站視頻:封裝一下

&#x1f4da;博客主頁&#xff1a;knighthood2001 ?公眾號&#xff1a;認知up吧 &#xff08;目前正在帶領大家一起提升認知&#xff0c;感興趣可以來圍觀一下&#xff09; &#x1f383;知識星球&#xff1a;【認知up吧|成長|副業】介紹 ??如遇文章付費&#xff0c;可先看…

大數據Hadoop之-工具HIVE(一)

大數據Hadoop之——數據倉庫Hive HIVE介紹Hive是基于Hadoop的一個數據倉庫(Data Aarehouse,簡稱數倉、DW),可以將結構化的數據文件映射為一張數據庫表,并提供類SQL查詢功能。是用于存儲、分析、報告的數據系統。 在Hadoop生態系統中,HDFS用于存儲數據,Yarn用于資源管理…

解釋Spring Bean的生命周期

Spring Bean的生命周期涉及到Bean的創建、配置、使用和銷毀的各個階段。理解這個生命周期對于編寫高效的Spring應用和充分利用框架的功能非常重要。下面是Spring Bean生命周期的主要步驟&#xff1a; 1. 實例化Bean Spring容器首先將使用Bean的定義&#xff08;無論是XML、注…

使用Golang調用騰訊云郵件模版發送郵件

文章目錄 一、騰訊云郵件模版創建1.1 發信域名配置1.2 發信地址設置1.3 發信模版設置 二、通過Golang發送郵件2.1 代碼示例2.2 代碼說明 三、常見問題3.1 UnsupportedRegion3.2 InvalidTemplateID 本文檔介紹了如何使用Golang編寫代碼&#xff0c;通過騰訊云郵件服務&#xff0…

【Linux】中的常見的重要指令(中)

目錄 一、man指令 二、cp指令 三、cat指令 四、mv指令 五、more指令 六、less指令 七、head指令 八、tail指令 一、man指令 Linux的命令有很多參數&#xff0c;我們不可能全記住&#xff0c;我們可以通過查看聯機手冊獲取幫助。訪問Linux手冊頁的命令是 man 語法: m…

白嫖免費圖床!CloudFlare R2太香了!

1 為啥要折騰搭建一個專屬圖床&#xff1f; 技術大佬寫博客都用 md 格式&#xff0c;要在多平臺發布&#xff0c;圖片就得有外鏈后續如博客遷移&#xff0c;國內博客網站如掘金&#xff0c;簡書&#xff0c;語雀等都做了防盜鏈&#xff0c;圖片無法遷移 2 為啥選擇CloudFlare…

對話太醫管家CEO徐晶:數字化技術正在為健康管理行業帶來新平衡丨數字思考者50人...

ITValue 鈦媒體特別專題策劃《數字思考者50人》&#xff1a;探訪中國深刻的數字化思考者群體。我們理解的“TechThinker”&#xff0c;涵蓋了中國數字化浪潮中的技術踐行者、政策制定者與投資決策者。在這場長達10年的乘風破浪中&#xff0c;每個人都在分享技術進步的果實&…

leetcode445-Add Two Numbers II

題目 給你兩個 非空 鏈表來代表兩個非負整數。數字最高位位于鏈表開始位置。它們的每個節點只存儲一位數字。將這兩數相加會返回一個新的鏈表。 你可以假設除了數字 0 之外&#xff0c;這兩個數字都不會以零開頭。 示例1&#xff1a; 輸入&#xff1a;l1 [7,2,4,3], l2 [5,6…

文件系統--軟硬鏈接

文章目錄 現象軟鏈接硬鏈接 現象 建立軟鏈接 建立硬鏈接 // 刪除軟硬鏈接都可以用 unlink 指令 unlink soft-link軟鏈接 軟鏈接是一個獨立的文件&#xff0c;因為有獨立的inode number 軟鏈接的內容&#xff1a;目標文件所對應的路勁字符串如果我們直接查看軟鏈接文件&#…

vue2vue3為什么el-table樹狀表格失效?

上圖所示&#xff0c;后端返回字段中有hasChildren字段。 解決樹狀表格失效方案&#xff1a; 從后端拿到數據后&#xff0c;遞歸去掉該字段&#xff0c;然后就能正常顯示。&#xff08;復制下方代碼&#xff0c;直接用&#xff09; 親測有效&#xff0c;vue2、vue3通用 /**…

如何運用多媒體,打造企業實力展示廳?

企業文化、產品是其長期發展的根本所在&#xff0c;為此越來越多的企業開始選擇運用多媒體互動&#xff0c;來打造企業多媒體展廳的方式&#xff0c;對企業文化、品牌形象、產品進行推廣宣傳&#xff0c;并在多媒體互動裝置的支持下&#xff0c;能讓客戶能夠快速且全面的了解企…