以Go的map是否併發安全爲例,介紹最權威的Go語言資料的使用方法

公衆號原文地址:https://mp.weixin.qq.com/s/KmT-Mu4YQDeaQY_95waj-Q

本篇目錄

  • 本篇目錄

  • 說明

  • 正確使用正確的資料

  • 最權威的 Go 語言資料是?

  • Go 語言的 map 是否是併發安全的?

  • 擴大搜索範圍

  • 找到答案不等於結束

  • 爲什麼要執着於一手資料?

  • 參考

說明

相比於細節,更在意知識框架的構建和完善,因此有時候對一些技術細節不是很清楚,只是知道如何找答案。最近要認真編碼,需要仔細考慮、敲定細節,趁此機會將 Go 語言的知識整理一下。

正確使用正確的資料

找到正確的資料、能夠正確的使用、正確的理解,是最關鍵的一步。除非是初學者,否則不要使用二手、三手和倒了無數手的資料,長期來看使用非一手資料,就是在浪費時間和引入錯誤。第一手的資料常常晦澀難懂,需要經過長時間的積累和沉澱,才能比較熟練的運用,剛開始的時候可以用二手、三手的資料幫助理解,但一定要逼迫自己在一手資料中找到解答,這個過程會極大地提升認知。

下面以Go 語言的 map 是否是併發安全的?爲例,演示一下 Go 語言的一手資料的用法。

最權威的 Go 語言資料是?

最權威的 Go 語言資料是什麼?Go 官網上的語言規範:The Go Programming Language Specification](https://upload-images.jianshu.io/upload_images/9231674-be49b617a53a68f0?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

這份資料可以理解爲 Go 語言的設計文檔之一,用表達式的方式對 Go 語言語法做出了最精確的定義,對 Go 語言特性的介紹也是最全面的。掌握了這一份資料,基本就掌握了 100% 的 Go 語言語法和大部分使用時應注意的細節。如果要了解 Go 語言的實現和一些更復雜的內容,需要去查閱其它文檔。

Go Spec 不容易看懂,裏面的每一個英文單詞都認識,但是聯在一起就看不明白了。因爲這份文檔是在從更高的維度描述 Go 語言,使用的是「編程入門」類書籍中沒有的術語,譬如Lexical elementsAssignability,就好像把「桌子」稱爲「一種用木材製作而成的傢俱」一樣。習慣了這種表達方式,並知道常見「稱呼」的含義後,會發現從這份文檔中找答案的效率非常高!

Go 語言的 map 是否是併發安全的?

先到 Go Spec 的 map 章節中閱讀 map 的細節:

image

map 是這樣定義的:

A map is an unordered group of elements of one type, called the element type,indexed by a set of unique keys of another type, called the key type.MapType     = "map" "[" KeyType "]" ElementType .KeyType     = Type .

從這一章裏得知:

  1. map 的 key 必須是可比(!=、==)的,不能是 function、map、slice,如果是接口類型,實際用到的值要可比;

  2. 沒有初始化的 map 變量的值是 nil,空 map 要用內置的 make 函數創建,未初始化的 map 不等於 空的map

  3. map 的 size 是無限制的,用 make 函數創建 map 時,設置的 capacity 不會限制 map 的 size,map 的容量是隨着 element 的插入動態增長的;

遺憾的是,在這裏沒有找到對併發操作的說明,用 golang.org 的站內搜索也沒找到相關的信息。

擴大搜索範圍

這時候用 Google 搜到了 stackoverflow 上的一篇問答:How safe are Golang maps for concurrent Read/Write operations?,引起注意的是這篇問答中的兩個鏈接:

  1. Go maps in action

  2. Go 1.6 Release Notes: Runtime

第一個連接是 Go 官方博客的一篇文章,第二個連接是 Go 1.6 的 Release Notes,然後又從第一個連接中找到了一個 Go 問答:

  1. Why are map operations not defined to be atomic?

把這個網頁下拉到頂部,發現了一個寶藏,Frequently Asked Questions (FAQ):

image

從上面幾個鏈接中的內容得知,Go 的 map 不是併發安全的,這是設計人員經過長期討論做出的決定:因爲大部分使用 map 的場景不需要併發讀寫,如果將 map 設計爲併發安全的,將降低大多數程序的性能。

只有在更新 map 的時候,map 纔是併發不安全的,全部是讀操作的併發是安全的。Go 1.6 對 map 的併發讀寫進行了更明確的規定:當一個協程正在對 map 進行寫操作的時候,不能有其它協程在對同一個 map 進行操作,讀和寫都不行。Go 的 runtime 會對 map 的併發讀寫進行監測,如果發現不安全的操作直接 crash。

找到答案不等於結束

上一節找到了Go 語言的 map 是否是併發安全的?這個問題的答案,但是事情沒有結束。

在上一節找答案的過程中,純粹是運氣好,遇到三篇預期外的文檔,一篇是 Go 的博客文章,一篇是 Go 的 Release Notes, 一篇是 Go 的 FAQ,這三篇權威的官方文檔給出了準確的答案。

權威資料得到擴充的同時第一個問題來了:這次是運氣好,用 Google 找到了這三篇官方的權威資料,如果下次查找其它問題的答案時,運氣沒這麼好該怎麼辦?

在 Go 的官網上溜達,最後發現這三篇文檔都可以通過 Documentation 頁面中的連接找到:

image

image

image

前面的操作也讓我們知道了, golang.org 站內搜索的結果是不全面的,用 Google 進行站內搜索能找到更多內容:

image

map safe site:golang.org

image

第二個問題是:既然基本類型 map 是併發讀寫不安全的,那麼要怎樣實現併發讀寫安全的 map 呢?自己加鎖實現不難,不過繼續查一下會發現,Go 標準庫中提供了一個通用的、併發讀寫安全的 type Map:

image

如果技術熱情高漲,可以繼續研究下標準庫中的實現有什麼特點,畢竟提出需求的 issues/18177 中說了一句:“RWMutex has some scaling issues,巴拉巴拉巴……”。僞技術迷就先告辭做任務去了 😭......

爲什麼要執着於一手資料?

因爲一手資料是最全的、並且會及時更新的資料。一本《Go 語言編程入門》出版之後,幾年內都不會變化,而幾年的時間對於一門編程語言來說很久了,如果只會從書本里找答案,那麼永遠不會知道最新的情況。

一手資料以外的資料,很難做到及時更新,所以我們纔會經常用 Google 、baidu 搜索到過時的解答和方案。有些非一手資料還是不靠譜的,漫天搜索、左右嘗試不僅耗費大量時間,而且即使碰巧找到正確方法把問題解決了,依舊雲裏霧裏不知其所以然,下次遇到同一個領域的問題,還要漫天搜索。

參考

  1. 李佶澳的博客

  2. Go: Map types

  3. The Go Programming Language Specification

  4. How safe are Golang maps for concurrent Read/Write operations?

  5. Go maps in action

  6. Go 1.6 Release Notes: Runtime

  7. Why are map operations not defined to be atomic?

  8. Frequently Asked Questions (FAQ)

  9. Go: Package sync

上一篇:正在或即將被使用的Go依賴包管理方法:Go Modules,Go 1.13的標準特性

關注後加作者微信

image

公衆號原文地址:https://mp.weixin.qq.com/s/KmT-Mu4YQDeaQY_95waj-Q

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章