golang中的执行规则
簡介
如果使用用戶級線程,我們就不得不既是指令下達(dá)者,又是指令執(zhí)行者。我們必須全權(quán)負(fù)責(zé)與用戶級線程有關(guān)的所有具體實現(xiàn)。
操作系統(tǒng)不但不會幫忙,還會要求我們的具體實現(xiàn)必須與它正確地對接,否則用戶級線程就無法被并發(fā)地,甚至正確地運行。畢竟我們編寫的所有代碼最終都需要通過操作系統(tǒng)才能在計算機(jī)上執(zhí)行
不過別擔(dān)心,Go 語言不但有著獨特的并發(fā)編程模型,以及用戶級線程 goroutine,還擁有強(qiáng)大的用于調(diào)度 goroutine、對接系統(tǒng)級線程的調(diào)度器。
這個調(diào)度器是 Go 語言運行時系統(tǒng)的重要組成部分,它主要負(fù)責(zé)統(tǒng)籌調(diào)配 Go 并發(fā)編程模型中的三個主要元素,即:G(goroutine 的縮寫)、P(processor 的縮寫)和 M(machine 的縮寫)
其中的 M 指代的就是系統(tǒng)級線程。而 P 指的是一種可以承載若干個 G,且能夠使這些 G 適時地與 M 進(jìn)行對接,并得到真正運行的中介。
從宏觀上說,G 和 M 由于 P 的存在可以呈現(xiàn)出多對多的關(guān)系。當(dāng)一個正在與某個 M 對接并運行著的 G,需要因某個事件(比如等待 I/O 或鎖的解除)而暫停運行的時候,調(diào)度器總會及時地發(fā)現(xiàn),并把這個 G 與那個 M 分離開,以釋放計算資源供那些等待運行的 G 使用。
而當(dāng)一個 G 需要恢復(fù)運行的時候,調(diào)度器又會盡快地為它尋找空閑的計算資源(包括 M)并安排運行。另外,當(dāng) M 不夠用時,調(diào)度器會幫我們向操作系統(tǒng)申請新的系統(tǒng)級線程,而當(dāng)某個 M 已無用時,調(diào)度器又會負(fù)責(zé)把它及時地銷毀掉。
正因為調(diào)度器幫助我們做了很多事,所以我們的 Go 程序才總是能高效地利用操作系統(tǒng)和計算機(jī)資源。程序中的所有 goroutine 也都會被充分地調(diào)度,其中的代碼也都會被并發(fā)地運行,即使這樣的 goroutine 有數(shù)以十萬計,也仍然可以如此
一定要注意,go函數(shù)真正被執(zhí)行的時間,總會與其所屬的go語句被執(zhí)行的時間不同。當(dāng)程序執(zhí)行到一條go語句的時候,Go 語言的運行時系統(tǒng),會先試圖從某個存放空閑的 G 的隊列中獲取一個 G(也就是 goroutine),它只有在找不到空閑 G 的情況下才會去創(chuàng)建一個新的 G
控制數(shù)量
用runtime.GOMAXPROCS(maxProcs)來控制P的數(shù)量
控制協(xié)程的數(shù)量
我們先創(chuàng)建一個通道,它的長度應(yīng)該與我們手動啟用的 goroutine 的數(shù)量一致。在每個手動啟用的 goroutine 即將運行完畢的時候,我們都要向該通道發(fā)送一個值
import ("fmt" )func main() {num := 10sign := make(chan struct{}, num)for i := 0; i < num; i++ {go func() {fmt.Println(i)sign <- struct{}{}}()}for j := 0; j < num; j++ {<-sign} }其中有一個細(xì)節(jié)你需要注意。我在聲明通道sign的時候是以chan struct{}作為其類型的。其中的類型字面量struct{}有些類似于空接口類型interface{},它代表了既不包含任何字段也不擁有任何方法的空結(jié)構(gòu)體類型。
注意,struct{}類型值的表示法只有一個,即:struct{}{}。并且,它占用的內(nèi)存空間是0字節(jié)。確切地說,這個值在整個 Go 程序中永遠(yuǎn)都只會存在一份。雖然我們可以無數(shù)次地使用這個值字面量,但是用到的卻都是同一個值
總結(jié)
以上是生活随笔為你收集整理的golang中的执行规则的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: golang计算单个协程占用内存
- 下一篇: golang中的WaitGroup