Go 為什麼不在語言層面支援 map 併發?

煎魚發表於2022-01-12

大家好,我是煎魚。

很多小夥伴學習 Go 語言的語法時,可能只是輕輕地看到過這個問題,結果一旦上手,多多少少一個組內總會碰到過幾次。

甚至會發現有一定年限的程式設計師也會遇到。有小夥伴疑惑了,這麼折騰,為什麼 Go 不直接在語言層面就支援 map 併發,那得有多香?

為什麼原生不支援

憑什麼 Go 官方還不支援,難不成太複雜了,效能太差了,到底是為什麼?

官方答覆原因如下(via @go faq):

  • 典型使用場景:map 的典型使用場景是不需要從多個 goroutine 中進行安全訪問。
  • 非典型場景(需要原子操作):map 可能是一些更大的資料結構或已經同步的計算的一部分。
  • 效能場景考慮:若是隻是為少數程式增加安全性,導致 map 所有的操作都要處理 mutex,將會降低大多數程式的效能。

核心來講就是:Go 團隊在經過了長時間的討論後,認為原生 map 更應適配典型使用場景。

如果為了小部分情況,將會導致大部分程式付出效能代價,決定了不支援原生的併發 map 讀寫。且在 Go1.6 起,增加了檢測機制,併發的話會導致異常。

為什麼要崩潰

前面有提到一點,在 Go1.6 起會進行原生 map 的併發檢測,這是一些人的 “噩夢”。

在此有人吐槽到:“明明給我拋個錯就好了,憑什麼要讓我的 Go 程式直接崩潰掉,分分鐘給我背個 P0”。

場景列舉

這裡我們假設一下,如果併發讀寫 map 是以下兩種場景:

  1. 產生 panic:程式 panic -> 預設走進 recover -> 沒有對併發 map 進行處理 -> map 存在髒資料 -> 程式使用髒資料 -> 產生**未知((影響。
  2. 產生 crash:程式 crash -> 直接崩潰 -> 保全資料(資料正常)-> 產生**明確((風險。

你會選擇哪一種方案呢?Go 官方在兩者的風險衡量中選擇了第二種。

無論是程式設計,還是人生。如何在隨機性中掌握確定性的部分,也是一門極大的哲學了。

let it crash

Go 官方團隊選擇的方式是業內經典的 “let it crash” 行為,很多程式語言中,都會將其奉行為設計哲學。

let it crash 是指工程師不必過分擔心未知的錯誤,而去進行面面俱到的防禦性編碼

這塊理念最經典的就是 erlang 了。

總結

在今天這篇文章中,我們介紹了 Go 語言為什麼不支援原生支援 map 併發,核心原因是大部分場景都不需要,從效能考慮上做的考慮。

直接讓併發讀寫 map 的原因,是從 “let it crash” 去考慮。這塊如果你想在自己的工程中避免這個情況,可以在 linter 等工具鏈加入競態檢測(-race),也可以避免這類風險。

你覺得 Go 這塊的設計考慮怎麼樣呢?歡迎在評論區留言和交流:)

若有任何疑問歡迎評論區反饋和交流,最好的關係是互相成就,各位的點贊就是煎魚創作的最大動力,感謝支援。

文章持續更新,可以微信搜【腦子進煎魚了】閱讀,本文 GitHub github.com/eddycjy/blog 已收錄,學習 Go 語言可以看 Go 學習地圖和路線,歡迎 Star 催更。

相關文章