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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

Go调度器系列(3)图解调度原理

發(fā)布時間:2023/12/31 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Go调度器系列(3)图解调度原理 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

如果你已經(jīng)閱讀了前2篇文章:《調(diào)度起源》和《宏觀看調(diào)度器》,你對G、P、M肯定已經(jīng)不再陌生,我們這篇文章就介紹Go調(diào)度器的基本原理,本文總結(jié)了12個主要的場景,覆蓋了以下內(nèi)容:

  • G的創(chuàng)建和分配。
  • P的本地隊列和全局隊列的負(fù)載均衡。
  • M如何尋找G。
  • M如何從G1切換到G2。
  • work stealing,M如何去偷G。
  • 為何需要自旋線程。
  • G進(jìn)行系統(tǒng)調(diào)用,如何保證P的其他G'可以被執(zhí)行,而不是餓死。
  • Go調(diào)度器的搶占。
  • 12場景

    提示:圖在前,場景描述在后。

    image

    上圖中三角形、正方形、圓形分別代表了M、P、G,正方形連接的綠色長方形代表了P的本地隊列。

    場景1:p1擁有g(shù)1,m1獲取p1后開始運行g(shù)1,g1使用go func()創(chuàng)建了g2,為了局部性g2優(yōu)先加入到p1的本地隊列。

    image

    場景2g1運行完成后(函數(shù):goexit),m上運行的goroutine切換為g0,g0負(fù)責(zé)調(diào)度時協(xié)程的切換(函數(shù):schedule)。從p1的本地隊列取g2,從g0切換到g2,并開始運行g(shù)2(函數(shù):execute)。實現(xiàn)了線程m1的復(fù)用

    image

    場景3:假設(shè)每個p的本地隊列只能存4個g。g2要創(chuàng)建了6個g,前4個g(g3, g4, g5, g6)已經(jīng)加入p1的本地隊列,p1本地隊列滿了。

    image

    藍(lán)色長方形代表全局隊列。

    場景4:g2在創(chuàng)建g7的時候,發(fā)現(xiàn)p1的本地隊列已滿,需要執(zhí)行負(fù)載均衡,把p1中本地隊列中前一半的g,還有新創(chuàng)建的g轉(zhuǎn)移到全局隊列(實現(xiàn)中并不一定是新的g,如果g是g2之后就執(zhí)行的,會被保存在本地隊列,利用某個老的g替換新g加入全局隊列),這些g被轉(zhuǎn)移到全局隊列時,會被打亂順序。所以g3,g4,g7被轉(zhuǎn)移到全局隊列。

    image

    場景5:g2創(chuàng)建g8時,p1的本地隊列未滿,所以g8會被加入到p1的本地隊列。

    image

    場景6在創(chuàng)建g時,運行的g會嘗試喚醒其他空閑的p和m執(zhí)行。假定g2喚醒了m2,m2綁定了p2,并運行g(shù)0,但p2本地隊列沒有g(shù),m2此時為自旋線程(沒有G但為運行狀態(tài)的線程,不斷尋找g,后續(xù)場景會有介紹)。

    [站外圖片上傳中...(image-d0a5f1-1554527569260)]

    場景7:m2嘗試從全局隊列(GQ)取一批g放到p2的本地隊列(函數(shù):findrunnable)。m2從全局隊列取的g數(shù)量符合下面的公式:

    n = min(len(GQ)/GOMAXPROCS + 1, len(GQ/2))

    公式的含義是,至少從全局隊列取1個g,但每次不要從全局隊列移動太多的g到p本地隊列,給其他p留點。這是從全局隊列到P本地隊列的負(fù)載均衡

    假定我們場景中一共有4個P,所以m2只從能從全局隊列取1個g(即g3)移動p2本地隊列,然后完成從g0到g3的切換,運行g(shù)3。

    image

    場景8:假設(shè)g2一直在m1上運行,經(jīng)過2輪后,m2已經(jīng)把g7、g4也挪到了p2的本地隊列并完成運行,全局隊列和p2的本地隊列都空了,如上圖左邊。

    全局隊列已經(jīng)沒有g(shù),那m就要執(zhí)行work stealing:從其他有g(shù)的p哪里偷取一半g過來,放到自己的P本地隊列。p2從p1的本地隊列尾部取一半的g,本例中一半則只有1個g8,放到p2的本地隊列,情況如上圖右邊。

    image

    場景9:p1本地隊列g(shù)5、g6已經(jīng)被其他m偷走并運行完成,當(dāng)前m1和m2分別在運行g(shù)2和g8,m3和m4沒有g(shù)oroutine可以運行,m3和m4處于自旋狀態(tài),它們不斷尋找goroutine。為什么要讓m3和m4自旋,自旋本質(zhì)是在運行,線程在運行卻沒有執(zhí)行g(shù),就變成了浪費CPU?銷毀線程不是更好嗎?可以節(jié)約CPU資源。創(chuàng)建和銷毀CPU都是浪費時間的,我們希望當(dāng)有新goroutine創(chuàng)建時,立刻能有m運行它,如果銷毀再新建就增加了時延,降低了效率。當(dāng)然也考慮了過多的自旋線程是浪費CPU,所以系統(tǒng)中最多有GOMAXPROCS個自旋的線程,多余的沒事做線程會讓他們休眠(見函數(shù):notesleep())。

    image

    場景10:假定當(dāng)前除了m3和m4為自旋線程,還有m5和m6為自旋線程,g8創(chuàng)建了g9,g8進(jìn)行了阻塞的系統(tǒng)調(diào)用,m2和p2立即解綁,p2會執(zhí)行以下判斷:如果p2本地隊列有g(shù)、全局隊列有g(shù)或有空閑的m,p2都會立馬喚醒1個m和它綁定,否則p2則會加入到空閑P列表,等待m來獲取可用的p。本場景中,p2本地隊列有g(shù),可以和其他自旋線程m5綁定。

    場景11:(無圖場景)g8創(chuàng)建了g9,假如g8進(jìn)行了非阻塞系統(tǒng)調(diào)用(CGO會是這種方式,見cgocall()),m2和p2會解綁,但m2會記住p,然后g8和m2進(jìn)入系統(tǒng)調(diào)用狀態(tài)。當(dāng)g8和m2退出系統(tǒng)調(diào)用時,會嘗試獲取p2,如果無法獲取,則獲取空閑的p,如果依然沒有,g8會被記為可運行狀態(tài),并加入到全局隊列。

    場景12:(無圖場景)Go調(diào)度在go1.12實現(xiàn)了搶占,應(yīng)該更精確的稱為請求式搶占,那是因為go調(diào)度器的搶占和OS的線程搶占比起來很柔和,不暴力,不會說線程時間片到了,或者更高優(yōu)先級的任務(wù)到了,執(zhí)行搶占調(diào)度。go的搶占調(diào)度柔和到只給goroutine發(fā)送1個搶占請求,至于goroutine何時停下來,那就管不到了。搶占請求需要滿足2個條件中的1個:1)G進(jìn)行系統(tǒng)調(diào)用超過20us,2)G運行超過10ms。調(diào)度器在啟動的時候會啟動一個單獨的線程sysmon,它負(fù)責(zé)所有的監(jiān)控工作,其中1項就是搶占,發(fā)現(xiàn)滿足搶占條件的G時,就發(fā)出搶占請求。

    場景融合

    如果把上面所有的場景都融合起來,就能構(gòu)成下面這幅圖了,它從整體的角度描述了Go調(diào)度器各部分的關(guān)系。圖的上半部分是G的創(chuàng)建、負(fù)債均衡和work stealing,下半部分是M不停尋找和執(zhí)行G的迭代過程。

    如果你看這幅圖還有些似懂非懂,建議趕緊開始看雨痕大神的Golang源碼剖析,章節(jié):并發(fā)調(diào)度。

    image

    總結(jié),Go調(diào)度器和OS調(diào)度器相比,是相當(dāng)?shù)妮p量與簡單了,但它已經(jīng)足以撐起goroutine的調(diào)度工作了,并且讓Go具有了原生(強大)并發(fā)的能力,這是偉大的。如果你記住的不多,你一定要記住這一點:Go調(diào)度本質(zhì)是把大量的goroutine分配到少量線程上去執(zhí)行,并利用多核并行,實現(xiàn)更強大的并發(fā)。

    下集預(yù)告

    下篇會是源碼層面的內(nèi)容了,關(guān)于源碼分析的書籍、文章可以先看起來了,先劇透一篇圖,希望閱讀下篇文章趕緊關(guān)注本公眾號。

    image

    推薦閱讀

    Go調(diào)度器系列(1)起源
    Go調(diào)度器系列(2)宏觀看調(diào)度器

    參考資料

    在學(xué)習(xí)調(diào)度器的時候,看了很多文章,這里列一些重要的:

  • The Go scheduler: https://morsmachine.dk/go-scheduler
  • Go's work-stealing scheduler: https://rakyll.org/scheduler/,中文翻譯版: https://lingchao.xin/post/gos-work-stealing-scheduler.html
  • Go夜讀:golang 中 goroutine 的調(diào)度: https://reading.developerlearning.cn/reading/12-2018-08-02-goroutine-gpm/
  • Scheduling In Go : Part I、II、III: https://www.ardanlabs.com/blog/2018/08/scheduling-in-go-part2.html,中文翻譯版: https://www.jianshu.com/p/cb6881a2661d
  • 雨痕大神的golang源碼剖析: github.com/qyuhen/book
  • 也談goroutine調(diào)度器: https://tonybai.com/2017/06/23/an-intro-about-goroutine-scheduler/
  • kavya的調(diào)度PPT: https://speakerdeck.com/kavya719/the-scheduler-saga
  • 搶占的設(shè)計提案,Proposal: Non-cooperative goroutine preemption: https://github.com/golang/proposal/blob/master/design/24543-non-cooperative-preemption.md
  • 如果這篇文章對你有幫助,請點個贊/喜歡,感謝
  • 本文作者:大彬
  • 如果喜歡本文,隨意轉(zhuǎn)載,但請保留此原文鏈接:http://lessisbetter.site/2019/04/04/golang-scheduler-3-principle-with-graph/
  • image

    總結(jié)

    以上是生活随笔為你收集整理的Go调度器系列(3)图解调度原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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