日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

golang的goroutine调度机制

發(fā)布時(shí)間:2024/3/26 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 golang的goroutine调度机制 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一直對(duì)goroutine調(diào)度機(jī)制很好奇,最近在看雨痕的golang分析,(基于go1.4)

覺(jué)豁然開(kāi)朗,受益匪淺;

去繁就簡(jiǎn),再加上自己的一些理解,整理了一下

~~

調(diào)度

主要基于三個(gè)基本對(duì)象上,GMP(定src/runtime/runtime.h文件中)

1.???? G代表一個(gè)goroutine對(duì)象,每次go調(diào)用的時(shí)候,都會(huì)創(chuàng)建一個(gè)G對(duì)

2.???? M代表一個(gè)程,每次創(chuàng)建一個(gè)M時(shí)候,都會(huì)有一個(gè)底層線創(chuàng)建;所有的G務(wù),最終還是在M執(zhí)

3.???? P代表一個(gè)理器,每一個(gè)運(yùn)行的M都必須綁定一個(gè)P,就像程必在么一個(gè)CPU核上執(zhí)行一

P的個(gè)數(shù)就是GOMAXPROCS(最大256),啟動(dòng)時(shí)固定的,一般不修改; M的個(gè)數(shù)和P的個(gè)數(shù)不一定一樣多(會(huì)有休眠的M或者不需要太多的M)(最大10000);每一個(gè)P保存著本地G任務(wù)隊(duì)列,也有一個(gè)全局G任務(wù)隊(duì)列;

如下圖所示


全局G任務(wù)隊(duì)列會(huì)和各個(gè)本地G任務(wù)隊(duì)列按照一定的策略互相交換(滿了,則把本地隊(duì)列的一半送給全局隊(duì)列)

P是用一個(gè)全局?jǐn)?shù)組(255)來(lái)保存的,并且維護(hù)著一個(gè)全局的P空閑鏈表

每次go調(diào)用的時(shí)候,都會(huì):

1.???? 創(chuàng)建一個(gè)G對(duì)象,加入到本地隊(duì)列或者全局隊(duì)

2.???? 如果有空P則創(chuàng)建一個(gè)M

3.???? M會(huì)啟動(dòng)一個(gè)底層線程,循環(huán)執(zhí)行能找到的G務(wù)

4.???? G務(wù)執(zhí)序是,先從本地隊(duì)列找,本地沒(méi)有從全局隊(duì)列找(一次性轉(zhuǎn)(全局G個(gè)數(shù)/P個(gè)數(shù))個(gè),再去其它P中找(一次性轉(zhuǎn)移一半),

5.???? 以上的G務(wù)執(zhí)行是按照隊(duì)序(也就是go調(diào)用的順序)執(zhí)行的。(這個(gè)地方是不是覺(jué)得很奇怪??

對(duì)于上面的第2-3步,創(chuàng)建一個(gè)M,其過(guò)程:

1.???? 先找到一個(gè)空P,如果沒(méi)有直接返回,(哈哈,個(gè)地方就保進(jìn)程不會(huì)占用超過(guò)自己設(shè)定的cpu個(gè)數(shù))

2.???? 調(diào)用系統(tǒng)api創(chuàng)建線程,不同的操作系統(tǒng),調(diào)用不一樣,其實(shí)就是和c語(yǔ)言創(chuàng)建過(guò)程是一致的,(windows用的是CreateThreadlinux用的是clone統(tǒng)調(diào)用),(*^__^*)嘻嘻……

3.???? 然后創(chuàng)建的個(gè)程里面才是真正做事的,循環(huán)執(zhí)G務(wù)

那就會(huì)有個(gè)問(wèn)題,如果一個(gè)系統(tǒng)調(diào)用或者G任務(wù)執(zhí)行太長(zhǎng),他就會(huì)一直占用這個(gè)線程,由于本地隊(duì)列的G任務(wù)是順序執(zhí)行的,其它G任務(wù)就會(huì)阻塞了,怎樣中止長(zhǎng)任務(wù)的呢?(這個(gè)地方我找了好久~o()o

這樣滴,啟動(dòng)的時(shí)候,會(huì)專門創(chuàng)建一個(gè)線程sysmon,用來(lái)監(jiān)控和管理,在內(nèi)部是一個(gè)循環(huán):

1.???? 記錄所有PG務(wù)計(jì)數(shù)schedtick,(schedtick會(huì)在每執(zhí)行一個(gè)G務(wù)增)

2.???? 如果檢查?schedtick一直沒(méi)有增,說(shuō)個(gè)P一直在執(zhí)行同一個(gè)G務(wù),如果超過(guò)一定的時(shí)間10ms),就在個(gè)G務(wù)信息里面加一個(gè)標(biāo)記

3.???? 然后個(gè)G務(wù)執(zhí)行的時(shí)候,如果遇到非內(nèi)聯(lián)函數(shù)調(diào)用,就會(huì)檢查一次個(gè)標(biāo)記,然后中斷自己,把自己加到隊(duì)列末尾,執(zhí)行下一個(gè)G

4.???? O(∩_∩)O哈哈~,如果沒(méi)有遇到非內(nèi)聯(lián)函數(shù)(有時(shí)候正常的小函數(shù)會(huì)被優(yōu)化成內(nèi)聯(lián)函數(shù))調(diào)用的,那就慘了,會(huì)一直執(zhí)個(gè)G務(wù),直到它自己結(jié)束;如果是個(gè)死循環(huán),并且GOMAXPROCS=1,恭喜你,夯住了!親測(cè),的確如此

對(duì)于一個(gè)G任務(wù),中斷后的恢復(fù)過(guò)程:

1.???? 中斷的時(shí)候?qū)⒓拇嫫骼锏?/span>棧信息,保存到自己的G對(duì)象里

2.???? 當(dāng)再次到自己執(zhí)時(shí),將自己保存的信息復(fù)制到寄存器里面,這樣就接著上次之后運(yùn)行了。 ~\(≧▽≦)/~

?

但是還有一個(gè)問(wèn)題,就是系統(tǒng)啟動(dòng)的過(guò)程,雨痕沒(méi)有說(shuō)的太明白,我一直有很多問(wèn)題都狠疑惑(第一個(gè)M怎么來(lái)的?,G怎么找到對(duì)應(yīng)的P等等),這個(gè)讓我蛋疼了好久~

不過(guò)我自己意淫了一下,補(bǔ)充在下面,歡迎大家指正

1.???? 統(tǒng)動(dòng)時(shí),首先跑的是主程,那第一個(gè)M應(yīng)該就是程吧(按照C語(yǔ)言的理解,嘿嘿),這里叫M1,可以看前面的

2.???? 然后這個(gè)主線程會(huì)第一個(gè)P1

3.???? 寫(xiě)的main函數(shù),其實(shí)是作一個(gè)goroutine來(lái)執(zhí)行的(雨痕說(shuō)的)

4.???? 也就是第一個(gè)P1就有了一個(gè)G1務(wù),然后第一個(gè)M1執(zhí)個(gè)G1務(wù)(也就是main函數(shù)),創(chuàng)個(gè)G1時(shí)候不用創(chuàng)M了,因經(jīng)有了M1

5.???? 這個(gè)main函數(shù)里面所有的goroutine,都定到當(dāng)前的M1對(duì)應(yīng)P1上,O(∩_∩)O哈哈~

6.???? 然后創(chuàng)main里的goroutine時(shí)候(比如G2),就會(huì)創(chuàng)建新的M2,新的M2里的初始P2的本地任務(wù)隊(duì)列是空的,會(huì)從P1里面取一些過(guò)來(lái),哈哈

7.???? 這樣兩個(gè)M1M2各自執(zhí)行自己的G務(wù),再依次往復(fù),下就圓滿~~~

?

綜上:

所以goroutine是按照搶占式調(diào)度的,一個(gè)goroutine最多執(zhí)行10ms就會(huì)換作下一個(gè)

這個(gè)和目前主流系統(tǒng)的的cpu調(diào)度類似(按照時(shí)間分片)

windows20ms

linux5ms800ms


到這里都差不多了,這些在雨痕的筆記里面都有更詳細(xì)的描述,不過(guò)很多地方比較凌亂,比較復(fù)雜,這里篩檢了很多,方便讀者理解

?

注意:

1.???? Golang編譯器也會(huì)嘗試進(jìn)行內(nèi)聯(lián),將小函數(shù)直接復(fù)制并編譯了內(nèi)聯(lián),盡量消除編譯器無(wú)法偵測(cè)dead code,利用gobuild -gcflags=-m編譯命令可以查看程序內(nèi)聯(lián)狀態(tài),不得不說(shuō)golang編譯工具鏈還是很強(qiáng)大的,十分有利于程序的優(yōu)化。

?

如果有任何疑問(wèn),歡迎提出,

隨時(shí)更新


(這篇文章是去年整理的,記錄公司內(nèi)部wiki上~)

總結(jié)

以上是生活随笔為你收集整理的golang的goroutine调度机制的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。