iT邦幫忙

第 11 屆 iT 邦幫忙鐵人賽

DAY 5
1

說明

Golang FAQ 就有提到,不同的goroutine對於同樣的map做讀寫操作可能會有什麼問題,很感謝剛開始接觸Golang的時候,朋友熱心的建議,先到FAQ好好用心閱讀一遍,對於Golang的設計概念與原理大體上有個了解。

內容提到多個goroutine對於同一個map做讀取資料是沒有問題,因為僅是讀取資料,但兩個以上的goroutine對同樣的map直接寫入資料可就不行,這是要非常注意的事情。

然後還有一些小細節,FAQ並沒有特別提到,在此將我們遇過的一些小經驗分享給大家。

目前遇到的錯誤大概有以下3種:

1. 多個goroutine 去寫同一個map

錯誤訊息: fatal error: concurrent map writes
說明: 這個狀況就如FAQ說明的,解決方法是在寫入之前加Lock,寫完之後Unlock。

2. 多個goroutine 同一個map,有人讀,有人在寫

錯誤訊息: fatal error: concurrent map read and map write
說明: 這個錯誤第一次遇到時,我有點感到訝異,因為FAQ沒有特別說明同時讀寫也不行,不過思考一下,也覺得這也是非常合理的,併發的當下同時讀和寫撞在一起該怎麼辦,解決方法也是加上鎖來處理。

3. 多個goroutine 同一個map,有人做iteration,有人在寫

錯誤訊息: 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大家也知道效能難免會受到影響,於是有沒有其他做法或者其他資料結構的使用方式,建議可以多斟酌一下。

下篇要分享一個是因為寫法上的隱藏陷阱,而這個陷阱大多時候根本不會發動。


上一篇
Day4 .[重災經驗篇] 常見的panic造成原因
下一篇
Day6 .[重災經驗篇] … 的傳入參數方式,自由度很大,但還是要小心
系列文
Let's Eat GO ! 實務開發雜談by Golang30

尚未有邦友留言

立即登入留言