Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

結束語|再聊Go 併發程式設計的價值和精進之路

你好,我是鳥窩。

本章導讀

                  課程收尾與能力進階路徑

[學會 API] -> [理解原理] -> [避坑除錯] -> [實務選型] -> [組合新解法]
      |            |            |             |              |
    會寫        看得懂底層      知道風險        做對取捨         能處理複雜場景

很高興和你一起度過了一個多月的時間,到了和你說再見的時候了。

在過去的這些年裡,我一直在研究 Go 併發程式設計,時間越久,越覺得,掌握 Go 併發原語是一件很有意思的事情。

很多剛開始學習併發原語的同學給我留言說:“使用 Go 寫併發程式很容易啊,為啥要學這麼多併發原語呢?”

如果你也有這樣的疑問,我的答案就是在這節課的封面圖中寫的那句話:“併發原語,初識時簡單,深交時複雜,熟識時又覺簡單。”這是我的真實體會。

如果你處於剛開始接觸併發原語的階段,你可能會覺得:“這挺好理解的呀,我一看就會了。”但是隨著學習的不斷深入,你會看到各種複雜的用法,各種潛在的坑,這些東西打破了初印象,你會陷入到“千頭萬緒”的境地。只要你不畏困難,持續學習,最後你就可以輕鬆地使用這些併發原語了。如果說最初的“簡單”是“初生牛犢不怕虎”的“簡單”,那麼“熟識”後的“簡單”,就是“撥雲見霧”的“簡單”。這也是,我在這門課裡想要帶你達到的狀態。

總之,使用 Go 寫併發程式很容易,使用 Go 寫好併發程式很不容易。

遺憾的是,很多人都沒有意識併發程式設計的複雜性,甚至還沒有意識到,併發程式設計錯誤帶來的嚴重後果。所以,我想跟你分享關於併發程式設計 Bug 的兩個小故事。

第一個故事,是我剛剛看到的澳大利亞交易所(ASX)的新系統在上線後崩潰的故事。

11 月 16 日中午,ASX 釋出宣告說,當天將休市,會在次日的正常時間重新開放。官方給出的關閉原因是“侷限於單個交易指令中交易多種證券(組合交易)的軟體問題,導致了市場資料不準確。”

雖然我並沒有看到這個 Bug 的細節,但是,從官方提供的關閉原因中,我們可以簡單地推斷出是“單個指令中交易多種證券的問題”,大機率是一個併發問題的 Bug。雖然經過一天的排查和修復,第二天這個交易所就恢復上線了。但是,耽誤一天的時間,損失也是非常大的。

類似的軟體 Bug,尤其是併發問題的 Bug,即使經過很長時間的測試,也不一定能被觸發和發現。可是一旦出現,就可能是一個一級的 Bug。

如果看完這個故事,你還沒有意識到併發程式設計的複雜性和併發問題的危害,我再給你講一個故事。

1997 年 7 月,NASA 的 Mars Pathfinder(火星探路者)在降落火星表面後不久,就因併發軟體中的一個缺陷受到了威脅。這是在飛行前的測試中發現的,但因為它只發生在某些沒有預料到的過載條件下,所以被給予了較低的優先順序。

但是,飛船開始採集氣象資料的時候,它所使用的 vxWorks 作業系統就出現了問題,不斷地重啟。這是經典的優先順序反轉的併發 Bug。

幸好工程師上傳了一小段 C 語言程式給飛船,在執行的時候,將優先順序繼承的互斥標誌從 false 改成了 true,才成功地解決了這個 Bug。

這次人為的忽視,險些釀成慘劇。所以,學好併發程式設計,是我們的重要責任。

那麼,該怎麼在編寫 Go 程式時,避免併發程式設計的 Bug 呢?在開篇詞裡,我講到了“兩大主線”,現在學完了所有內容之後,你會發現,其實可以抽象成“三部曲”:

  1. 全面地掌握 Go 併發程式設計的知識,不遺漏任何的知識點;
  2. 熟悉每一個併發原語的功能和實作,在面對併發場景時,能夠高效地選出最適合的併發原語;
  3. 多看看別人踩的坑,避免自己再掉進相同的坑裡。

在前面的課程中,我講的所有內容,都是為了幫助你輕鬆地完成這三個目標。在課程的最後,我還想再給你多交代幾句。

學完這門課,並不代表你已經掌握了 Go 併發程式設計的知識。Go 併發程式設計的知識廣、內容深,現在你再回顧前面的知識,可能已經遺忘了一大半了。即使你現在記得很清楚,等過一段時間,再提到這些知識點,你也可能答不上來。

所以,學完這門課並不是一件一勞永逸的事情,你要在空閒的時候多複習下前面的內容。怎麼複習呢?你可能也注意到了,每講完一個併發原語,課程裡都有一張知識導圖,這些圖既可以幫助你梳理知識主線,也可以幫助你高效地複習。

你可以對照著圖中的核心要點,去回顧我們學習的重要內容,如果感覺有些地方比較陌生了,就及時回去複習下。另外,你也可以做一些摘錄,並且寫上你自己的收穫和思考。學習過不等於能輸出,你一定要記住這句話。

另外,這門課的核心是講 Go 併發原語的知識,並沒有涉及到 Go 併發模型和排程的事情。這不是說,我認為這部分內容不重要,而是很多大牛已經把這些內容寫得很清楚、很明白了。如果你對這方面的知識還不太熟悉,可以搜尋關鍵字“golang gpm schedule”,你會看到很多資料。你讀幾篇,就明白了。如果要推薦的話,我建議你重點讀一讀歐長坤的 《Go 語言原本》的 併發排程,這一篇的邏輯非常順暢,能看出非常多的經驗。

當然,我還想再給你推薦一些補充資料,如果你還有餘力,可以再擴充套件一下知識面。

首先是一本書,名字是“Concurrency in Go”。這是第一本全面介紹 Go 併發程式設計的圖書。書中介紹了併發程式設計的背景知識、常見的原語和併發模式。我印象最深的,就是書裡對 Channel 的介紹,比如 Channel 是粘合 goroutine 的膠水,而 select 是粘合 Channel 的膠水。這樣形象的說法,可以幫助你快速地學到精髓。

除此之外,Go 官方部落格列出的一些技術分享,比如Go Concurrency Patterns、Advanced Go Concurrency Patterns,都是不錯的閱讀材料,我建議你好好讀一讀。

好了,關於結課後的學習方法,我就說到這裡。在這節課的最後,我特別想再和你分享我自己的兩個心得。

第一,開放的心態,可以拓展你的人生邊界。

我始終認為,一個人衰老的標誌,不是指他的容貌經歷了太多歲月的刻畫,而是他的內心封閉了,不再接收新的知識、新的事物。

在一些技術交流會上,我聽到一些開發者說,Go 併發程式設計很簡單,有什麼可學的?遇到這種不是技術討論的話題,我一般只會說:“你說得對。”

我當然認同我們應該把核心精力用在眼下有價值的事情上,在自己擅長的領域裡深耕,但是我更相信,開放心會讓你的人生與眾不同。如果你碰見了新技術的發展,即使不需要深入地學習,也要儘量花時間去了解一下,也許這些新的東西,就是你人生的轉折點。

我之前就是一直使用 Java、Scala,後來才開始瞭解 Go,但是,很顯然,Go 給我的人生帶來了不一樣的東西。如果不是深入研究 Go,我就沒有機會開設這麼一門課了。

第二,無數人想要你的注意力,但只有你能決定你把它放在哪裡。

我們總說這個時代是資訊爆炸的時代,其實,資訊爆炸就意味著千萬的資訊傳送者想要佔用你的注意力。你一定要保持謹慎,不要毫無感知地把你的時間扔給無價值、無意義的資訊。

如果說上一條是讓你延伸注意力的觸角,那麼這一條,就是讓你收縮注意力的觸角,但這兩者並不矛盾,因為側重點不同。“延伸”還是“收縮”,取決於你自己想要擁有的人生的樣子,只有你能決定。我能做的,就是提醒你,要開放,也要謹慎。

雖然很捨不得,但還是要跟你說再見了。在課程的最後,我給你準備一份結課問卷,希望你花 1 分鐘時間,點選下面的圖片填寫一下。如果你的建議被採納,我將會給你贈送一個護腕墊或者價值 99 元的課程閱碼。期待你的暢所欲言。