go語言學習筆記-基礎篇

一、go語言

1、go支持語言級的併發,併發的最小邏輯單位是goroutine,goroutine就是go為了實現併發提供的用戶態線程這 種用戶態線程是運行在內核態線程(OS線程)之上,當我們創建大量的goroutine並且同時運行在一個或多個內核態線程上時(內核線程與goroutine是m:n的對應關係),就需要一個調度器來維護管理這些goroutine,確保所有goroutine的都有相對公平的機會使用CPU

2、goroutine和內核OS線程的映射關係是m:n,這樣多個goroutine就可以在多個內核線程上運行,goroutine的

切換大部分的場景下都沒有走OS線程的切換所帶來的開銷,這樣整個運行效率相比OS線程的調度會高很多,但隨之帶來的問題就是goroutine的調度模型非常複雜

二、調度模型scheduler

1、go的調度模型主要有幾個主要的實體:G M P schedt

1.1、G:代表一個goroutine實體,他有自己的棧內存,instruction和pointer和一些相關信息(比如等待的

channel等等),是用於調度器調度的實體

1.2、M:代表一個真正的內核OS線程,和POSIX裡的thread查不多,屬於真正執行指令的人

1.3、P:代表M調度的上下文,可以吧他看做一個局部的調度器,調度協程go代碼在一個內核線程上跑。P是實現協程與內核線程的m:n映射關係的關鍵,P的上限是通過系統變量runtime.GOMAXPROCS (numLogicalProcessors)來控制的。go啟動是更新這一個值,一般不修改,P的數量也代表了go代碼執行的併發度,即可以併發多少個goroutine

1.4、schedt:runtime全局調度是使用的數據結構,這其實是個殼,裡面主要有M的全局idle隊列,P的全局idle隊列,一個全局的就緒的G隊列以及一個runtime全局調度器級別的鎖。當對M或P等做一些費局部調度器的操作是,一般需要先鎖住全局調度器

2、我們通過 go func()來創建一個goroutine;

3、有兩個存儲goroutine的隊列,一個是局部調度器P的local queue、一個是全局調度器數據模型schedt的global queue。新創建的goroutine會先保存在local queue,如果local queue已經滿了就會保存在全局的global queue;

4、goroutine只能運行在M中,一個M必須持有一個P,M與P是1:1的關係。M會從P的local queue彈出一個Runable狀態的goroutine來執行,如果P的local queue為空,就會執行work stealing;

5、一個M調度goroutine執行的過程是一個loop;

6、當M執行某一個goroutine時候如果發生了syscall或則其餘阻塞操作,M會阻塞,如果當前有一些G在執行,runtime會把這個線程M從P中摘除(detach),然後再創建一個新的操作系統的線程(如果有空閒的線程可用就複用空閒線程)來服務於這個P;

7、當M系統調用結束時候,這個goroutine會嘗試獲取一個空閒的P執行,並放入到這個P的local queue。如果獲取不到P,那麼這個線程M會park它自己(休眠), 加入到空閒線程中,然後這個goroutine會被放入schedt的global queue。

8、M本身是無狀態的,M是否是空閒態僅根據他是否存在於調度器的空閒M列表

9、runtime管轄的M會在GC任務執行的時候被停止,這個時候系統會對M的屬性做某些必要的重置,並吧M放置入全局調度器的空閒M列表因為調度器在需要一個未被使用的M時,運行是系統會先去這個空閒列表獲取M,只有都沒有時才會創建M。注意:空閒列表不是那個全局列表

10、P是使G能夠在M中運行的關鍵,GO的runtime適當地讓P與不同的M建立或者斷開聯繫,以似得P中的那些可運行的G能夠在需要的時候獲得運行時機

11、每個P中都有一個可運行G隊列及自由G隊列。自由G隊列包含了很多已經完成的G,隨著被運行完成的G的積攢到一定程度後,runtime會把其中的部分G轉移到全局調度器的自由G隊列

12、G需要綁定在M上才能運行,M需要綁定P才能運行

三、用戶級協程Goroutine

0、foroutine類似與線程,但並非線程。可以理解成虛擬線程,go運行是會參與調度goroutine,並將goroutine合理分配到每個CPU中,最大限度地使用CPU的性能

1、go語言的併發是基於goroutine

2、goroutine是建立在線程之上的輕量級抽象。他允許我們以非誠低的代價在同一個地址空間中並行執行多個函數或方法相比於線程,他的創建和銷燬的代價要小很多,並且他的調度 是獨立於線程的,在go中創建一個goroutine非常簡單使用“go”關鍵子即可

3、注意:和線程一樣,go的主函數其實也是跑在一個goroutine中,並不會等待其他goroutine結束,如果主goroutine結束了,所有其他goroutine都將結束

四、goroutine和線程的區別

1、goroutine相比與線程運行不會更快,他只是增加了更多的併發性,當一個goroutine被阻塞(比如IO等待),go的scheduler會調度其他可執行的goroutine運行

2、相較於線程來說,goroutine有一下一些優點

goroutine所需要的內存通常只有2kb,而線程則需1MB

創建與銷燬開銷更小

切換開銷更小,這也是goroutine能實現高併發的主要原因,線程的調度方式是搶佔式的,如果一個線程的執行時間超過了分配給他的時間,就會被其他可執行的線程搶佔,在切換過程中需要保存/恢復所有寄存器的信息,比如16個通用寄存器。而goroutine的調度是協同模式的,他不直接與操作系統內核打交道,當goroutine進行切換的時候,只有很少量的寄存器需要保存和恢復

3、goroutine的調度方式是協同模式的,在協同調度中沒有時間片的概念,為了並行執行goroutine,調度器會在已下幾個時間段進行切換

channel接受或這發送會造成阻塞的消息

當一個新的goroutine被創建時

可以造成阻塞的系統調用,如文件和網絡操作

垃圾回收

五、通信管道channel


1、goroutine運行在相同的地址空間,因此訪問共享內存必須做同步,goroutine通過通信來共享內存,而不是共享內存來通信

2、channel是一個對應make創建的底層數據結構的引用,當我們複製一個channel用於函數參數傳遞時,我們只是拷貝了一個channel

引用,因此調用者和被調用者將引用同一個channel對象

3、定義一個channel時,也需要定義發送到channel的值的類型,channel可以使用內置的make函數來創建

4、channel分兩類,一非緩衝類型,即channel無法緩衝元素,發送一個必須接收一個。如果連續發送兩個

第二次一定會被阻塞,對於緩衝類型怎要寬鬆些

5、有關channel的關閉

關閉一個未初始化的channel會產生panic

重複關閉同一個channel會產生panic

向一個已關閉的channel中發送消息會產生panic

從已關閉的channel讀取消息不會產生panic,且能獨處channel中還未被讀取的消息,

若消息均已讀出。則會讀到類型的0值。從一個已經關閉的channel中讀取消息永遠

不會阻塞,並且會返回一個為false的ok-idiom,可以判斷channel是否關閉

關閉channel會產生一個廣播機制,所有向channel讀取消息的goroutine都會收到消息

六、依賴管理

1、godep是go語言官方提供的管理工具

2、安裝godep工具:go get github.com/tools/godep 下載成功後會在GOPATH的bin目錄下生成godep.exe可執行文件,需配置環境變量

3、如何使用:godep help [命令名稱]

命令 作用

godep save 將依賴包的信息保存到 Godeps.json 文件中

godep go 使用保存的依賴項運行 go 工具

godep get 下載並安裝指定的包

godep path 打印依賴的 GOPATH 路徑

godep restore 在 GOPATH 中拉取依賴的版本

godep update 更新選定的包或 go 版本

godep diff 顯示當前和以前保存的依賴項集之間的差異

godep version 查看版本信息

4、GOPROXY代理服務配置,翻牆下載依賴包

5、代理服務地址:goproxy.io goproxy.cn

Windows配置:go env -w GOPROXY=https://goproxy.cn,direct

macos或Linux:export GOPROXY=https://goproxy.cn

6、使用go get命令下載指定版本的依賴包

執行go get 命令,在下載依賴包的同時還可以指定依賴包的版本。

運行go get -u命令會將項目中的包升級到最新的次要版本或者修訂版本;

運行go get -u=patch命令會將項目中的包升級到最新的修訂版本;

運行go get [包名]@[版本號]命令會下載對應包的指定版本或者將對應包升級到指定的版本。

七、編譯和運行

1、go build 命令可以將GO語言程序代碼編譯成二進制的可執行文件,但需要我們手動運行該二進制文件

2、go run 命令則是在編譯後會直接運行go語言程序,編譯過程中會產生一個臨時文件,但不會生成可執行文件

很適合用來調試程序

八、Goland安裝

http://c.biancheng.net/view/6124.html


分享到:


相關文章: