蔡超:入門 Go 語言必須跨越的五個思維誤區

你好,我是蔡超,現在是Mobvista 技術副總裁,前亞馬遜(中國)首席軟件架構師,極客時間《Go語言從入門到實戰》視頻課程的作者。

在2018年的QCon北京全球軟件開發大會上,我做了題爲《講給 Java / C++開發者的Go高效編程》的主題演講,會後跟大家交流,發現許多人對Go語言的學習有需求和熱情。但大家的普遍問題就是:不知道該如何高效的入門 Go語言,尤其是有編程基礎的開發者,一不小心就會陷入思維的誤區。

因此,今天我就來梳理一下Go語言入門學習的過程中,你容易產生的五個思維誤區,幫你掌握在Go語言與其他編程語言的不同之處。

一、重新思考面向對象程序的設計和實現

首先,你要先理解Go語言面向對象編程是具有特殊性的。當然,“Go是不是支持面向對象“這本身就是一個值得思考的話題,官方文檔給出的答案是“Yes and no” 。雖說 Go 語言中是有類型的存在,也允許面向對象的編程風格,但卻沒有類型層次結構。

Go語言中沒有對類型繼承提供支持,而是通過複合來進行擴展,並通過類型嵌入來簡化複合的使用。很多人會把類型嵌入看成是Go中的繼承機制,但是類型嵌入並不支持最基本的繼承特性:

  • 子類替換

  • 方法重載(override)

Go語言中的接口機制與其他語言截然不同,實現接口的類型完全不依賴於接口定義。接口作爲方法簽名的集合,任何類型的方法集中只要擁有與之對應的全部方法,就表示它實現了該接口。只有深刻理解這些,才能更好發揮Go的生產力特性。

不支持繼承,特殊的接口類型,這些都會要求我們重新思考設計和編程實現。

戳此可獲取:Go語言官方文檔

二、改變傳統GC(垃圾收集器)語言的思維模式

Go是一個非常特殊的語言,即追求簡單性又追求高效率,Go既內置支持GC(垃圾收集器),又支持指針對內存的直接訪問。其他支持GC的語言,比如在Java中,由於希望對開發者可以屏蔽內存管理,所以語言中沒有提供指針的直接訪問。爲了提高數據訪問和傳遞的效率,編程語言根據不同的情況,通過約束採用值傳遞或引用傳遞,來減少數據複製。

而Go比較簡單地統一採用值傳遞,但提供指針機制,因此用戶可以自己來選擇數據的傳遞方式,要引用傳遞時可以通過傳遞指針來完成。

所以在編碼時,你要考慮充分指針,提高數據訪問效率,減少內存複製並編寫GC友好的代碼。

三、重新思考程序的錯誤處理機制

Go語言的錯誤處理機制中既不支持現在主流的異常模式,同時也與傳統的C程序通過返回值返回錯誤狀態不同,Go語言支持返回多個值,可以同時返回結果和錯誤狀態。

  • 以下是Java語言示例:
try{
...
}catch(XXException e){
       //錯誤處理
}
  • 以下是Go語言示例:
if v, err = callSomeFn(); err!=nil{
   //錯誤處理
}

Go的error處理方式一直以來都是爭論的焦點,很多開發者認爲Go的錯誤處理機制似乎回到了70年代,使得錯誤處理代碼冗長且重複。而Go的設計者則認爲try-catch-finally的結構導致異常處理與控制流程的耦合,從而使程序結構發生了混亂。

Go的設計者當初選擇返回值這種錯誤處理機制,而不是try-catch這種機制,主要是考慮到前者適用於大型軟件,後者更適合小程序。因此,程序變大的時候,try-catch會讓錯誤處理更加冗長繁瑣,也就容易出錯。try-catch-finally會慫恿程序員標註過多普通錯誤,諸如打開文件失敗之類的異常,使得程序更加繁瑣。

這就決定了你必須重新思考錯誤處理的編程模式,因爲這樣的代碼是Go語言中非常常見的。

四、思考和學習使用CSP併發模型

與主流語言通過共享內存來進行併發控制方式不同,Go語言採用了CSP模式。這是一種用於描述兩個獨立的併發實體通過共享的通訊 Channel(管道)進行通信的併發模型。很多使用過Erlang等基於Actor模式的程序員,會誤認爲Go和這些語言的模式是一樣的。

而實際上,Go的CSP模式與常見的Actor模式(如:Erlang語言就採用了Actor模式)也有不少差異,例如:

  • 和Actor的直接通訊不同,CSP模式則是通過Channel進行通訊的,更鬆耦合一些;
  • Go中channel是有容量限制並且獨立於處理Groutine,而如Erlang,Actor模式中的mailbox容量是無限的,接收進程也總是被動地處理消息。

image

Actor模式和CSP模式區別圖

所以,要用好Go語言,一定要思考和學習使用CSP來高效的實現我們常見併發任務。

五、理解Goroutine的調度機制

Goroutine 是Go語言的招牌特性之一,較之線程是非常輕量級的。但是,如果你不瞭解其中的機制,僅僅按照線程的套路來使用,就發揮不出來Goroutine的優勢,甚至還會導致很多性能問題。

Goroutine有着和Java線程完全不同的調度機制,Java線程模型中線程和KSE(Kernel space Entity)是1:1的關係,一個用戶線程對應一個KSE。而Groutine和KSE是多對多的對應關係。雖然,Groutine的調度機制不如,由內核直接調度的線程機制效率那麼高,但是由於Groutine間的切換可以不涉及內核級切換,所以代價小很多。

CSP併發模型、Goroutine的調度機制是Go語言入門學習的過程中的重點和難點,我會在《Go語言從入門到實戰》進階篇中進行詳細的講解,敬請期待。

拓展閱讀:
5個步驟,編寫你的第一個 Go 程序

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