Go語言的九大核心特性主要有哪些?

Go語言之所以厲害,是因為它在服務端的開發中,總能抓住程序員的痛點,以最直接、簡單、高效、穩定的方式來解決問題。這裡給大家講解一下Go的核心特性。

Go語言的九大核心特性主要有哪些?

併發編程

Go語言在併發編程方面比絕大多數語言要簡潔得多,這一點是其最大亮點之一,也是其在未來進入高併發高性能場景的重要籌碼。

不同於傳統的多進程或多線程,golang的併發執行單元是一種稱為goroutine的協程。

由於在共享數據場景中會用到鎖,再加上GC,其併發性能有時不如異步複用IO模型,因此相對於大多數語言來說,golang的併發編程簡單比並發性能更具賣點。

很多語言都支持多線程、多進程編程,但遺憾的是,實現和控制起來並不是那麼令人感覺輕鬆和愉悅。Golang不同的是,語言級別支持協程(goroutine)併發(協程又稱微線程,比線程更輕量、開銷更小,性能更高),操作起來非常簡單,語言級別提供關鍵字(go)用於啟動協程,並且在同一臺機器上可以啟動成千上萬個協程。

協程經常被理解為輕量級線程,一個線程可以包含多個協程,共享堆不共享棧。協程間一般由應用程序顯式實現調度,上下文切換無需下到內核層,高效不少。協程間一般不做同步通訊,

而golang中實現協程間通訊有兩種:

1)共享內存型,即使用全局變量+mutex鎖來實現數據共享;

2)消息傳遞型,即使用一種獨有的channel機制進行異步通訊。

對比JAVA的多線程和GO的協程實現,明顯更直接、簡單。這就是GO的魅力所在,以簡單、高效的方式解決問題,關鍵字go,或許就是GO語言最重要的標誌。

內存回收(GC)

從C到C++,從程序性能的角度來考慮,這兩種語言允許程序員自己管理內存,包括內存的申請和釋放等。因為沒有垃圾回收機制所以C/C++運行起來速度很快,但是隨著而來的是程序員對內存使用上的很謹小慎微的考慮。因為哪怕一點不小心就可能會導致“內存洩露”使得資源浪費或者“野指針”使得程序崩潰等,儘管C++11後來使用了智能指針的概念,但是程序員仍然需要很小心的使用。後來為了提高程序開發的速度以及程序的健壯性,java和C#等高級語言引入了GC機制,即程序員不需要再考慮內存的回收等,而是由語言特性提供垃圾回收器來回收內存。但是隨之而來的可能是程序運行效率的降低。

GC過程是:先stop the world,掃描所有對象判活,把可回收對象在一段bitmap區中標記下來,接著立即start the world,恢復服務,同時起一個專門gorountine回收內存到空閒list中以備複用,不物理釋放。物理釋放由專門線程定期來執行。

GC瓶頸在於每次都要掃描所有對象來判活,待收集的對象數目越多,速度越慢。一個經驗值是掃描10w個對象需要花費1ms,所以儘量使用對象少的方案,比如我們同時考慮鏈表、map、slice、數組來進行存儲,鏈表和map每個元素都是一個對象,而slice或數組是一個對象,因此slice或數組有利於GC。

GC性能可能隨著版本不斷更新會不斷優化,這塊沒仔細調研,團隊中有HotSpot開發者,應該會借鑑jvm gc的設計思想,比如分代回收、safepoint等。

內存自動回收,再也不需要開發人員管理內存

開發人員專注業務實現,降低了心智負擔

只需要new分配內存,不需要釋放

內存分配

初始化階段直接分配一塊大內存區域,大內存被切分成各個大小等級的塊,放入不同的空閒list中,對象分配空間時從空閒list中取出大小合適的內存塊。內存回收時,會把不用的內存重放回空閒list。空閒內存會按照一定策略合併,以減少碎片。

編譯

編譯涉及到兩個問題:編譯速度和依賴管理

目前Golang具有兩種編譯器,一種是建立在GCC基礎上的Gccgo,另外一種是分別針對64位x64和32位x86計算機的一套編譯器(6g和8g)。

依賴管理方面,由於golang絕大多數第三方開源庫都在github上,在代碼的import中加上對應的github路徑就可以使用了,庫會默認下載到工程的pkg目錄下。

另外,編譯時會默認檢查代碼中所有實體的使用情況,凡是沒使用到的package或變量,都會編譯不通過。這是golang挺嚴謹的一面。

網絡編程

由於golang誕生在互聯網時代,因此它天生具備了去中心化、分佈式等特性,具體表現之一就是提供了豐富便捷的網絡編程接口,比如socket用net.Dial(基於tcp/udp,封裝了傳統的connect、listen、accept等接口)、http用http.Get/Post()、rpc用client.Call('class_name.method_name', args, &reply),等等。

高性能HTTP Server

函數多返回值

在C,C++中,包括其他的一些高級語言是不支持多個函數返回值的。但是這項功能又確實是需要的,所以在C語言中一般通過將返回值定義成一個結構體,或者通過函數的參數引用的形式進行返回。而在Go語言中,作為一種新型的語言,目標定位為強大的語言當然不能放棄對這一需求的滿足,所以支持函數多返回值是必須的。

函數定義時可以在入參後面再加(a,b,c),表示將有3個返回值a、b、c。這個特性在很多語言都有,比如python。

這個語法糖特性是有現實意義的,比如我們經常會要求接口返回一個三元組(errno,errmsg,data),在大多數只允許一個返回值的語言中,我們只能將三元組放入一個map或數組中返回,接收方還要寫代碼來檢查返回值中包含了三元組,如果允許多返回值,則直接在函數定義層面上就做了強制,使代碼更簡潔安全。

語言交互性

語言交互性指的是本語言是否能和其他語言交互,比如可以調用其他語言編譯的庫。

在Go語言中直接重用了大部份的C模塊,這裡稱為Cgo.Cgo允許開發者混合編寫C語言代碼,然後Cgo工具可以將這些混合的C代碼提取並生成對於C功能的調用包裝代碼。開發者基本上可以完全忽略這個Go語言和C語言的邊界是如何跨越的。

golang可以和C程序交互,但不能和C++交互。可以有兩種替代方案:1)先將c++編譯成動態庫,再由go調用一段c代碼,c代碼通過dlfcn庫動態調用動態庫(記得export LD_LIBRARY_PATH);2)使用swig(沒玩過)

異常處理

golang不支持try...catch這樣的結構化的異常解決方式,因為覺得會增加代碼量,且會被濫用,不管多小的異常都拋出。golang提倡的異常處理方式是:

普通異常:被調用方返回error對象,調用方判斷error對象。

嚴重異常:指的是中斷性panic(比如除0),使用defer...recover...panic機制來捕獲處理。嚴重異常一般由golang內部自動拋出,不需要用戶主動拋出,避免傳統try...catch寫得到處都是的情況。當然,用戶也可以使用panic('xxxx')主動拋出,只是這樣就使這一套機制退化成結構化異常機制了。

其他一些有趣的特性

類型推導:類型定義:支持var abc = 10這樣的語法,讓golang看上去有點像動態類型語言,但golang實際上時強類型的,前面的定義會被自動推導出是int類型。

作為強類型語言,隱式的類型轉換是不被允許的,記住一條原則:讓所有的東西都是顯式的。

簡單來說,Go是一門寫起來像動態語言,有著動態語言開發效率的靜態語言。

一個類型只要實現了某個interface的所有方法,即可實現該interface,無需顯式去繼承。

Go編程規範推薦每個Interface只提供一到兩個的方法。這樣使得每個接口的目的非常清晰。另外Go的隱式推導也使得我們組織程序架構的時候更加靈活。在寫JAVA/C++程序的時候,我們一開始就需要把父類/子類/接口設計好,因為一旦後面有變更,修改起來會非常痛苦。而Go不一樣,當你在實現的過程中發現某些方法可以抽象成接口的時候,你直接定義好這個接口就OK了,其他代碼不需要做任何修改,編譯器的自動推導會幫你做好一切。

不能循環引用:即如果a.go中import了b,則b.go要是import a會報import cycle not allowed。好處是可以避免一些潛在的編程危險,比如a中的func1()調用了b中的func2(),如果func2()也能調用func1(),將會導致無限循環調用下去。

defer機制:在Go語言中,提供關鍵字defer,可以通過該關鍵字指定需要延遲執行的邏輯體,即在函數體return前或出現panic時執行。這種機制非常適合善後邏輯處理,比如可以儘早避免可能出現的資源洩漏問題。

可以說,defer是繼goroutine和channel之後的另一個非常重要、實用的語言特性,對defer的引入,在很大程度上可以簡化編程,並且在語言描述上顯得更為自然,極大的增強了代碼的可讀性。

“包”的概念:和python一樣,把相同功能的代碼放到一個目錄,稱之為包。包可以被其他包引用。main包是用來生成可執行文件,每個程序只有一個main包。包的主要用途是提高代碼的可複用性。通過package可以引入其他包。

編程規範:GO語言的編程規範強制集成在語言中,比如明確規定花括號擺放位置,強制要求一行一句,不允許導入沒有使用的包,不允許定義沒有使用的變量,提供gofmt工具強制格式化代碼等等。奇怪的是,這些也引起了很多程序員的不滿,有人發表GO語言的XX條罪狀,裡面就不乏對編程規範的指責。要知道,從工程管理的角度,任何一個開發團隊都會對特定語言制定特定的編程規範,特別像Google這樣的公司,更是如此。GO的設計者們認為,與其將規範寫在文檔裡,還不如強制集成在語言裡,這樣更直接,更有利用團隊協作和工程管理。

交叉編譯:比如說你可以在運行 Linux 系統的計算機上開發運行 Windows 下運行的應用程序。這是第一門完全支持 UTF-8 的編程語言,這不僅體現在它可以處理使用 UTF-8 編碼的字符串,就連它的源碼文件格式都是使用的 UTF-8 編碼。


分享到:


相關文章: