Go語言也稱為 Golang,是由 Google 公司開發的一種靜態強類型、編譯型、并發型、并具有垃圾回收功能的編程語言。
接下來從幾個方面來具體介紹一下Go語言的特性。
## 語法簡單
拋開語法樣式不談,單就類型和規則而言,Go 與 C99、C11 相似之處頗多,這也是Go語言被冠以“NextC”名號的重要原因。
Go語言的語法處于簡單和復雜的兩極。C語言簡單到你每寫下一行代碼,都能在腦中想象出編譯后的模樣,指令如何執行,內存如何分配,等等。而 C 的復雜在于,它有太多隱晦而不著邊際的規則,著實讓人頭疼。相比較而言,Go 從零開始,沒有歷史包袱,在汲取眾多經驗教訓后,可從頭規劃一個規則嚴謹、條理簡單的世界。
Go語言的語法規則嚴謹,沒有歧義,更沒什么黑魔法變異用法。任何人寫出的代碼都基本一致,這使得Go語言簡單易學。放棄部分“靈活”和“自由”,換來更好的維護性,我覺得是值得的。
將“++”、“--”從運算符降級為語句,保留指針,但默認阻止指針運算,帶來的好處是顯而易見的。還有,將切片和字典作為內置類型,從運行時的層面進行優化,這也算是一種“簡單”。
## 并發模型
時至今日,并發編程已成為程序員的基本技能,在各個技術社區都能看到諸多與之相關的討論主題。在這種情況下Go語言卻一反常態做了件極大膽的事,從根本上將一切都并發化,運行時用 Goroutine 運行所有的一切,包括 main.main 入口函數。
可以說,Goroutine 是 Go 最顯著的特征。它用類協程的方式來處理并發單元,卻又在運行時層面做了更深度的優化處理。這使得語法上的并發編程變得極為容易,無須處理回調,無須關注線程切換,僅一個關鍵字,簡單而自然。
搭配 channel,實現 CSP 模型。將并發單元間的數據耦合拆解開來,各司其職,這對所有糾結于內存共享、鎖粒度的開發人員都是一個可期盼的解脫。若說有所不足,那就是應該有個更大的計劃,將通信從進程內拓展到進程外,實現真正意義上的分布式。
## 內存分配
將一切并發化固然是好,但帶來的問題同樣很多。如何實現高并發下的內存分配和管理就是個難題。好在 Go 選擇了 tcmalloc,它本就是為并發而設計的高性能內存分配組件。
可以說,內存分配器是運行時三大組件里變化最少的部分。刨去因配合垃圾回收器而修改的內容,內存分配器完整保留了 tcmalloc 的原始架構。使用 cache 為當前執行線程提供無鎖分配,多個 central 在不同線程間平衡內存單元復用。在更高層次里,heap 則管理著大塊內存,用以切分成不同等級的復用內存塊。快速分配和二級內存平衡機制,讓內存分配器能優秀地完成高壓力下的內存管理任務。
在最近幾個版本中,編譯器優化卓有成效。它會竭力將對象分配在棧上,以降低垃圾回收壓力,減少管理消耗,提升執行性能。可以說,除偶爾因性能問題而被迫采用對象池和自主內存管理外,我們基本無須參與內存管理操作。
## 垃圾回收
垃圾回收一直是個難題。早年間,[Java]()就因垃圾回收低效被嘲笑了許久,后來 Sun 連續收納了好多人和技術才發展到今天。可即便如此,在 Hadoop 等大內存應用場景下,垃圾回收依舊捉襟見肘、步履維艱。
相比 Java,Go 面臨的困難要更多。因指針的存在,所以回收內存不能做收縮處理。幸好,指針運算被阻止,否則要做到精確回收都難。
每次升級,垃圾回收器必然是核心組件里修改最多的部分。從并發清理,到降低 STW 時間,直到 Go 的 1.5 版本實現并發標記,逐步引入三色標記和寫屏障等等,都是為了能讓垃圾回收在不影響用戶邏輯的情況下更好地工作。盡管有了努力,當前版本的垃圾回收算法也只能說堪用,離好用尚有不少距離。
## 靜態鏈接
Go 剛發布時,靜態鏈接被當作優點宣傳。只須編譯后的一個可執行文件,無須附加任何東西就能部署。這似乎很不錯,只是后來風氣變了。連著幾個版本,編譯器都在完善動態庫 buildmode 功能,場面一時變得有些尷尬。
暫不說未完工的 buildmode 模式,靜態編譯的好處顯而易見。將運行時、依賴庫直接打包到可執行文件內部,簡化了部署和發布操作,無須事先安裝運行環境和下載諸多第三方庫。這種簡單方式對于編寫系統軟件有著極大好處,因為庫依賴一直都是個麻煩。
## 標準庫
功能完善、質量可靠的標準庫為編程語言提供了充足動力。在不借助第三方擴展的情況下,就可完成大部分基礎功能開發,這大大降低了學習和使用成本。最關鍵的是,標準庫有升級和修復保障,還能從運行時獲得深層次優化的便利,這是第三方庫所不具備的。
Go 標準庫雖稱不得完全覆蓋,但也算極為豐富。其中值得稱道的是 net/http,僅須簡單幾條語句就能實現一個高性能 Web Server,這從來都是宣傳的亮點。更何況大批基于此的優秀第三方 Framework 更是將 Go 推到 Web/Microservice 開發標準之一的位置。
當然,優秀第三方資源也是語言生態圈的重要組成部分。近年來崛起的幾門語言中,Go 算是獨樹一幟,大批優秀作品頻繁涌現,這也給我們學習 Go 提供了很好的參照。
## 工具鏈
完整的工具鏈對于日常開發極為重要。Go 在此做得相當不錯,無論是編譯、格式化、錯誤檢查、幫助文檔,還是第三方包下載、更新都有對應的工具。其功能未必完善,但起碼算得上簡單易用。
內置完整測試框架,其中包括單元測試、性能測試、代碼覆蓋率、數據競爭,以及用來調優的 pprof,這些都是保障代碼能正確而穩定運行的必備利器。
除此之外,還可通過環境變量輸出運行時監控信息,尤其是垃圾回收和并發調度跟蹤,可進一步幫助我們改進算法,獲得更佳的運行期表現。
- Golang語言之旅
- 第一章:初始小節以及安裝
- 一:Golang語言特性
- 二:Windows上安裝Go語言開發包
- 三:在Mac OS上安裝Go語言開發包
- 第二章:GO語言注意事項
- 一:Dos的常用指令
- 第三章:Go初識小菜
- 一:Go語言之變量與常量
- 二:Go內置值-引用類型
- 三:基本的數據類型
- 四:字符串(char)
- 五:布爾類型(bool)
- 六:字符串類型(string)
- 七:基本數據類型的默認值
- 八:基本數據類型的互相轉換
- 九:基本數據類型和string類型的相互轉換
- 十:Golang指針
- 十一:值類型和引用類型
- 十二:標識符和命名規范
- 十三:系統保留關鍵字and預定義標識符
- 十四:fmt常用方法解析
- 第四章:Go運算符
- 一:運算符的基本介紹
- 二:算術運算符
- 2.1:算數運算符細節
- 三:關系運算符
- 3.1:關系運算符細節
- 四:邏輯運算符
- 4.1:邏輯運算符細節及案例
- 五:Go賦值運算符
- 5.1:案例演示賦值運算符的基本使用
- 5.2:賦值運算符的特點
- 六:Go位運算符
- 七:其他運算符
- 八:運算符的優先級
- 九:控制臺輸入語句
- 十:進制
- 十一:位運算
- 第五章:流程控制大綱
- 一:if語句
- 二:switch語句
- 三:for循環
- 第六章:函數-包-錯誤處理
- 一:Go函數
- 二:Go包
- 三:匿名函數
- 四:閉包
- 五:函數defer
- 六:函數參數的傳遞方式
- 七:變量的作用域
- 八:時間和日期相關函數
- 九:new和recover異常
- 十:數組(Array)切片(Section)
- 十一:切片(slice)
- 十二:3 數組的排序和查找
- 第七章:Map
- 第一節:Map基礎認識
- 第二節:Map初始化和make
- 第三節:Map增刪改查
- 第四節:Map的切片
- 第五節:Map的注意事項
- 第八章:面向對象(上)
- 第一節:結構體(值類型)
- 第二節:方法
- 第三節:面向對象編程應用實例
- 第九章:面向對象(下)
- 第一節:面向對象之抽象
- 第二節:面向對象之繼承
- 第三節:面向對象之多態
- 第四節:接口
- 第十章:文件操作
- 第一節:文件基本介紹
- 第二季:寫文件實例操作
- 第三節:JSON
- 第十一章:單元測試
- 第一節:單元測試介紹
- 第二節:單元測試案例
- 第三節:單元測試總結
- 第四節:單元測試案例
- 第十二章:goroutine和channel
- 第一節:goroutine基本介紹
- 第二節:goroutine入門案例
- 第三節:goroutione調度模型
- 第四節:Golang設置運行的CPU數量
- 第十二章:channel
- 第一節:channel基本介紹
- 第二節:channel基本使用
- 第三節:channel案例演示
- 第四節:channel 使用的注意事項
- 第五節:channel練習題
- 第六節:channel的遍歷和關閉
- 第七節:goroutione和channel結合
- 第八節:channel細節處理
- 第十二章:并發模式
- 第十三章:反射reflect
- 第一節:反射基本介紹
- 第二節:反射重要的函數和概念
- 第三節:反射快速入門案例
- 第四節:反射注意事項
- 第五節:反射練習題
- 第六節:反射最佳實踐