<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          都說 Go 可以開啟成千上萬的 Goroutine,那調(diào)度器是怎么處理核上任務(wù)分配的?

          共 624字,需瀏覽 2分鐘

           ·

          2020-08-28 18:30

          點(diǎn)擊上方藍(lán)色“Go語(yǔ)言中文網(wǎng)”關(guān)注我們,領(lǐng)全套Go資料,每天學(xué)習(xí)?Go?語(yǔ)言

          Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee French.

          ?? 這篇文章基于 Go 1.13 版本。

          在 Go 中創(chuàng)建 gorotine 既方便又快捷,然而 Go 在同一時(shí)間內(nèi)最多在一個(gè)核上運(yùn)行一個(gè) gorotine,因此需要一種方法來存放其他的 gorotine,從而確保處理器(processor)負(fù)載均衡。

          Goroutine 隊(duì)列

          Go 使用兩級(jí)隊(duì)列來管理等待中的 goroutine,分別為本地隊(duì)列和全局隊(duì)列。每一個(gè)處理器都擁有本地隊(duì)列,而全局隊(duì)列是唯一的,且能被所有的處理器訪問到:

          Global and local queues

          每個(gè)本地隊(duì)列都有最大容量,為 256。在容量滿了之后,任意新到來的 Goroutine 都會(huì)被放置到全局隊(duì)列。下面的例子是,生產(chǎn)了上千個(gè) Goroutine 的程序:

          func?main()?{
          ???var?wg?sync.WaitGroup

          ???for?i?:=?0;i?2000?;i++?{
          ??????wg.Add(1)
          ??????Go?func()?{
          ?????????a?:=?0

          ?????????for?i?:=?0;?i?1e6;?i++?{
          ????????????a?+=?1
          ?????????}

          ?????????wg.Done()
          ??????}()
          ???}

          ???wg.Wait()
          }

          下面是擁有兩個(gè)處理器的調(diào)度器追蹤數(shù)據(jù)(traces):

          Details of the local and global queues

          追蹤數(shù)據(jù)通過 runqueue 展示了全局隊(duì)列中 Goroutine 的數(shù)量,以及方括號(hào)中 [3 256] 的本地隊(duì)列 goroutine 數(shù)量(分別為 P0P1)。當(dāng)本地隊(duì)列滿了,積壓了 256 個(gè)等待中的 goroutine 后,下一個(gè) Goroutine 會(huì)被壓棧到全局隊(duì)列中,正如我們從 runqueue 看到的數(shù)量增長(zhǎng)一樣。

          Goroutine 僅在本地隊(duì)列滿載之后才會(huì)加入到全局隊(duì)列;它也會(huì)在 Go 往調(diào)度器中批量注入時(shí)被加到全局隊(duì)列,例如,網(wǎng)絡(luò)輪詢器(network poller) 或者在垃圾回收期間等待的 goroutine。

          下面是上一個(gè)例子的圖示:

          Local queues have up to 256 goroutines

          不過,我們還想知道,為什么本地隊(duì)列 P0 在上一個(gè)列子中不為空。因?yàn)?Go 使用了其他策略確保每個(gè)處理器都有任務(wù)處理。

          任務(wù)竊取

          如果處理器沒有任務(wù)可處理,它會(huì)按以下規(guī)則來執(zhí)行,直到滿足某一條規(guī)則:

          • 從本地隊(duì)列獲取任務(wù)
          • 從全局隊(duì)列獲取任務(wù)
          • 從網(wǎng)絡(luò)輪詢器獲取任務(wù)
          • 從其它的處理器的本地隊(duì)列竊取任務(wù)

          在我們前面的例子中,主函數(shù)在 P1 上運(yùn)行并創(chuàng)建 goroutine。當(dāng)?shù)谝慌?gourinte 已經(jīng)進(jìn)入了 P1 的本地隊(duì)列時(shí),P0 正在尋找任務(wù)。然而,它的本地隊(duì)列,全局隊(duì)列,以及網(wǎng)絡(luò)輪詢器都是空的。最后的解決方法是從 P1 中竊取任務(wù)。

          Work-stealing by P0

          下面是調(diào)度器在發(fā)生任務(wù)竊取前后的追蹤數(shù)據(jù):

          Work-stealing by P0

          追蹤數(shù)據(jù)展示了,處理器是如何從其它處理器中竊取任務(wù)的。它從(其他處理器的)本地隊(duì)列中取走一半的 goroutine;在七個(gè) Goroutine 中,偷走了四個(gè) —— 其中一個(gè)立馬在 P0 執(zhí)行,剩下的放到本地隊(duì)列。現(xiàn)在處理器間工作處于負(fù)載良好的狀態(tài)。這能通過執(zhí)行 tracing 來確認(rèn):

          goroutine 被合理地分發(fā),然后因?yàn)闆]有 I/O,goroutine 被鏈?zhǔn)綀?zhí)行而不需要切換。我們現(xiàn)在看一下,當(dāng)出現(xiàn)例如涉及到文件操作等 I/O 時(shí),會(huì)發(fā)生什么。

          I/O 與全局隊(duì)列

          一起看下涉及到文件操作的例子:

          func?main()?{
          ???var?wg?sync.WaitGroup

          ???for?i?:=?0;i?20?;i++?{
          ??????wg.Add(1)
          ??????Go?func()?{
          ?????????a?:=?0
          ?????????for?i?:=?0;?i?1e6;?i++?{
          ????????????a?+=?1
          ????????????if?i?==?1e6/2?{
          ???????????????bytes,?_?:=?ioutil.ReadFile(`add.txt`)
          ???????????????inc,?_?:=?strconv.Atoi(string(bytes))
          ???????????????a?+=?inc
          ????????????}
          ?????????}
          ?????????wg.Done()
          ??????}()
          ???}

          ???wg.Wait()
          }

          變量 a 隨著時(shí)間以文件的字節(jié)數(shù)增加,下面是新的追蹤數(shù)據(jù):

          在這個(gè)例子中,我們能看到每一個(gè) Goroutine 不只被一個(gè)處理器處理。在系統(tǒng)調(diào)用的情況下,當(dāng)調(diào)用完成后,Go 使用網(wǎng)絡(luò)輪詢器從全局隊(duì)列中把 gouroutine 取回來。這里是 Goroutine #35 的一個(gè)示意圖:

          I/O operations put the work back to the global queue

          當(dāng)一個(gè)處理器能從全局隊(duì)列中獲取任務(wù),第一個(gè)可用的處理器( P) 會(huì)執(zhí)行這個(gè) goroutine。這個(gè)行為解釋了,為什么一個(gè) Goroutine 能在不同的處理器中運(yùn)行,也展示了 Go 是如何讓空閑的處理器資源運(yùn)行 goroutine,從而進(jìn)行系統(tǒng)調(diào)用的優(yōu)化。


          via: https://medium.com/a-journey-with-go/go-work-stealing-in-go-scheduler-d439231be64d

          作者:Vincent Blanchon[1]譯者:LSivan[2]校對(duì):polaris1119[3]

          本文由 GCTT[4] 原創(chuàng)編譯,Go 中文網(wǎng)[5] 榮譽(yù)推出

          參考資料

          [1]

          Vincent Blanchon: https://medium.com/@blanchon.vincent

          [2]

          LSivan: https://github.com/LSivan

          [3]

          polaris1119: https://github.com/polaris1119

          [4]

          GCTT: https://github.com/studygolang/GCTT

          [5]

          Go 中文網(wǎng): https://studygolang.com/



          推薦閱讀


          學(xué)習(xí)交流 Go 語(yǔ)言,掃碼回復(fù)「進(jìn)群」即可


          站長(zhǎng) polarisxu

          自己的原創(chuàng)文章

          不限于 Go 技術(shù)

          職場(chǎng)和創(chuàng)業(yè)經(jīng)驗(yàn)


          Go語(yǔ)言中文網(wǎng)

          每天為你

          分享 Go 知識(shí)

          Go愛好者值得關(guān)注


          瀏覽 43
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  留学生苏琪和外国男友第二季 | 久久久久久久久黄色 | 亚洲网站视频在线观看 | 天天燥日日燥 | 中文字幕一区二区三区免费2023 |