一文徹底弄懂go中的調(diào)度GMP
先說躬厌,協(xié)程的本質(zhì)是用戶態(tài)的線程铛碑,用戶對其有控制權限狠裹,內(nèi)存占用少,切換代價低亚茬。
再來解釋一下MPG是什么意思酪耳。
M代表內(nèi)核線程,所有的G都要放在M上才能運行刹缝。
P代表控制器,調(diào)度G到M上颈将,其維護了一個隊列梢夯,存儲了所有需要它來調(diào)度的G。
G代表一個go routine單元晴圾。
補充幾點常見的調(diào)度策略:
1颂砸,如果某個M陷入阻塞呢?
當一個OS線程M由于io操作而陷入阻塞,假設此時G0正跑在了M上人乓,那么M上綁定的P就會帶著余下的所有G去尋找新的M勤篮。當M恢復過來時,一般情況下色罚,會從別的M上拿過來一個P碰缔,并把原先跑在其上的G0放到P的隊列中,從而運行G0戳护。如果金抡,沒有拿到可用的P的話,就把G0放入到全局global runqueue隊列中腌且,使G0等待被調(diào)度梗肝,然后M進入線程緩存。所有的P也會周期性的檢查global runqueue并運行其中的goroutine铺董,否則global runqueue上的goroutine永遠無法執(zhí)行巫击。
2,如果有的M較忙精续,有的M較閑呢喘鸟?
此時P所分配的任務G很快就執(zhí)行完了(分配不均),這就導致了這個處理器P很忙驻右,但是其他的P還有任務什黑。此時,P首先會去global runqueue取G堪夭。但是愕把,如果global runqueue沒有任務G了,那么P不得不從其他的P里拿一些G來執(zhí)行森爽。一般來說恨豁,如果P從其他的P那里要拿任務的話,一般就拿run queue的一半爬迟,這就確保了每個OS線程都能充分的使用橘蜜。
3,如果一個G運行時間過長付呕,導致隊列中后續(xù)G都無法運行呢计福?
啟動的時候,會專門創(chuàng)建一個線程sysmon徽职,用來監(jiān)控和管理象颖,在內(nèi)部是一個循環(huán)。首先姆钉,記錄所有P的G任務計數(shù)schedtick说订,schedtick會在每執(zhí)行一個G任務后遞增抄瓦。如果檢查到 schedtick一直沒有遞增,說明這個P一直在執(zhí)行同一個G任務陶冷,如果超過一定的時間(10ms)钙姊,就在這個G任務的棧信息里面加一個標記。然后這個G任務在執(zhí)行的時候埂伦,如果遇到非內(nèi)聯(lián)函數(shù)調(diào)用煞额,就會檢查一次這個標記,然后中斷自己赤屋,把自己加到隊列末尾立镶,執(zhí)行下一個G。如果沒有遇到非內(nèi)聯(lián)函數(shù)(有時候正常的小函數(shù)會被優(yōu)化成內(nèi)聯(lián)函數(shù))調(diào)用的話类早,那就慘了媚媒,會一直執(zhí)行這個G任務,直到它自己結(jié)束涩僻;如果是個死循環(huán)缭召,并且GOMAXPROCS=1的話,恭喜你逆日,夯住了嵌巷!親測,的確如此室抽。
4搪哪,一個G由于調(diào)度被中斷,此后如何恢復坪圾?
中斷的時候?qū)⒓拇嫫骼锏臈P畔⑾郏4娴阶约旱腉對象里面。當再次輪到自己執(zhí)行時兽泄,將自己保存的棧信息復制到寄存器里面漓概,這樣就接著上次之后運行了。 (≧▽≦)/
————————————————
版權聲明:本文為CSDN博主「jigetage」的原創(chuàng)文章病梢,遵循CC 4.0 BY-SA版權協(xié)議胃珍,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/jigetage/java/article/details/103350180