Go之旅:Goroutine 的開啟和退出

??本文基于 Go 1.14。
在 Go 中,協(xié)程就是一個包含程序運(yùn)行時的信息的結(jié)構(gòu)體,如棧,程序計(jì)數(shù)器,或者它當(dāng)前的 OS 線程。調(diào)度器還必須注意 Goroutine 的開始和退出,這兩個階段需要謹(jǐn)慎管理。
如果你想了解更多關(guān)于棧和程序計(jì)數(shù)器的信息,我推薦你閱讀我的文章 Go:協(xié)程切換時涉及到哪些資源?[1]。
開啟
開啟一個協(xié)程的處理過程相當(dāng)簡單。我們用一個程序作為例子:

main 函數(shù)在打印信息之前開啟了一個協(xié)程。由于協(xié)程會有自己的運(yùn)行時間,因此 Go 通知運(yùn)行時配置一個新協(xié)程,意味著:
創(chuàng)建棧 收集當(dāng)前程序計(jì)數(shù)器或調(diào)用方數(shù)據(jù)的信息 更新協(xié)程內(nèi)部數(shù)據(jù),如 ID 或 狀態(tài)
然而,協(xié)程沒有立即獲取運(yùn)行時狀態(tài)。新創(chuàng)建的協(xié)程被加入到了本地隊(duì)列的最前端,會在 Go 調(diào)度的下一周期運(yùn)行。下面是現(xiàn)在這種狀態(tài)的示意圖:

把協(xié)程放在隊(duì)列的前端,這樣它就會在當(dāng)前協(xié)程運(yùn)行之后第一個運(yùn)行。如果有工作竊取發(fā)生,它不是在當(dāng)前線程就是在另一個線程運(yùn)行。
我推薦你閱讀我的文章 Go: Go 調(diào)度器中的工作竊取[2]來獲取更多信息。
在匯編指令中也可以看到協(xié)程的創(chuàng)建過程:

協(xié)程被創(chuàng)建并被加入到本地協(xié)程隊(duì)列后,它直接執(zhí)行主函數(shù)的下一個指令。
退出
協(xié)程結(jié)束時,為了不浪費(fèi) CPU 資源,Go 必須調(diào)度另一個協(xié)程。這也使協(xié)程可以在以后復(fù)用。
在我的文章 Go: 協(xié)程怎么復(fù)用?[3]中你可以找到更多信息。
然而,Go 需要一個能識別到協(xié)程結(jié)束的方法。這個方法是在協(xié)程創(chuàng)建時控制的。創(chuàng)建協(xié)程時,Go 在將程序計(jì)數(shù)器設(shè)置為協(xié)程真實(shí)調(diào)用的函數(shù)之前,將堆棧設(shè)置為名為 goexit 的函數(shù)。這個技巧可以使協(xié)程在結(jié)束時必須調(diào) goexit 函數(shù)。下面的程序可以使我們理解得更形象:

根據(jù)輸出信息進(jìn)行堆棧追蹤:
/path/to/src/main.go:16
/usr/local/go/src/runtime/asm_amd64.s:1373
用匯編寫的 asm_amd64 文件包含這個函數(shù):

之后,Go 切換到 g0 調(diào)度另一個協(xié)程。
我們也可以調(diào)用 runtime.Goexit() 來手動終止協(xié)程:

這個函數(shù)首先運(yùn)行 defer 中的函數(shù),然后會運(yùn)行前面在協(xié)程退出時我們看到的那個函數(shù)。
via: https://medium.com/a-journey-with-go/go-how-does-a-goroutine-start-and-exit-2b3303890452
作者:Vincent Blanchon[4]譯者:lxbwolf[5]校對:polaris1119[6]
本文由 GCTT[7] 原創(chuàng)編譯,Go 中文網(wǎng)[8] 榮譽(yù)推出
參考資料
Go:協(xié)程切換時涉及到哪些資源?: https://medium.com/a-journey-with-go/go-what-does-a-goroutine-switch-actually-involve-394c202dddb7
[2]Go: Go 調(diào)度器中的工作竊取: https://medium.com/a-journey-with-go/go-work-stealing-in-go-scheduler-d439231be64d
[3]Go: 協(xié)程怎么復(fù)用?: https://medium.com/a-journey-with-go/go-how-does-go-recycle-goroutines-f047a79ab352
[4]Vincent Blanchon: https://medium.com/@blanchon.vincent
[5]lxbwolf: https://github.com/lxbwolf
[6]polaris1119: https://github.com/polaris1119
[7]GCTT: https://github.com/studygolang/GCTT
[8]Go 中文網(wǎng): https://studygolang.com/
推薦閱讀
