Golang FAQ 就有提到,不同的goroutine對於同樣的map做讀寫操作可能會有什麼問題,很感謝剛開始接觸Golang的時候,朋友熱心的建議,先到FAQ好好用心閱讀一遍,對於Golang的設計概念與原理大體上有個了解。
內容提到多個goroutine對於同一個map做讀取資料是沒有問題,因為僅是讀取資料,但兩個以上的goroutine對同樣的map直接寫入資料可就不行,這是要非常注意的事情。
然後還有一些小細節,FAQ並沒有特別提到,在此將我們遇過的一些小經驗分享給大家。
目前遇到的錯誤大概有以下3種:
錯誤訊息: fatal error: concurrent map writes
說明: 這個狀況就如FAQ說明的,解決方法是在寫入之前加Lock,寫完之後Unlock。
錯誤訊息: fatal error: concurrent map read and map write
說明: 這個錯誤第一次遇到時,我有點感到訝異,因為FAQ沒有特別說明同時讀寫也不行,不過思考一下,也覺得這也是非常合理的,併發的當下同時讀和寫撞在一起該怎麼辦,解決方法也是加上鎖來處理。
錯誤訊息: fatal error: concurrent map iteration and map write
說明: 我的理解是當使用for迴圈或range map的時候,也是算做某種讀取map吧,起初遇到這個錯誤訊息也是蠻傻眼的,好吧,咱們不經一事,不長一智,這個教訓就學起來了,可是要注意一點,Lock要加鎖和解鎖的範圍可能要包覆整個for迴圈,這樣會使得效能非常差,建議是重新考慮一下架構設計。
可以試加個檢測race condition的指令,先試跑看看
-race
go run main.go -race
結果如下,你可以稍微事先發現程式發生什麼狀況了
==================
WARNING: DATA RACE
Write at 0x00c4200881b0 by goroutine 10:
runtime.mapassign_fast64()
/usr/local/Cellar/go/1.10.2/libexec/src/runtime/hashmap_fast.go:522 +0x0
main.main.func1()
/Users/shen_su/go/src/golang-learning/tt_22_map_copy/main.go:42 +0x64
Previous read at 0x00c4200881b0 by main goroutine:
runtime.mapiterinit()
/usr/local/Cellar/go/1.10.2/libexec/src/runtime/hashmap.go:691 +0x0
main.main()
/Users/shen_su/go/src/golang-learning/tt_22_map_copy/main.go:48 +0x12e
Goroutine 10 (running) created at:
main.main()
/Users/shen_su/go/src/golang-learning/tt_22_map_copy/main.go:38 +0xe5
==================
這種錯誤一但在執行時期發生,是『fatal error』,並非panic,一但發生crash了沒得救,程式上線前務必檢查和測試過。
程式設計時,有沒有需要使用到不同goroutine去操作同個map,這點可能也是需要好好省思的,這樣的使用方式,難免要加上Lock上去,加上了Lock大家也知道效能難免會受到影響,於是有沒有其他做法或者其他資料結構的使用方式,建議可以多斟酌一下。
下篇要分享一個是因為寫法上的隱藏陷阱,而這個陷阱大多時候根本不會發動。