<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』Go 的搶占式調(diào)度(文末有彩蛋)

          共 4396字,需瀏覽 9分鐘

           ·

          2021-05-15 12:59


          • 原文地址:https://dtyler.io/articles/2021/03/29/goroutine_preemption_en/

          • 原文作者:Hidetatsu

          • 本文永久鏈接:https://github.com/gocn/translator/blob/master/2021/w19_Preemption_in_Go.md

          • 譯者:lsj1342

          • 校對:guzzsek、fivezh



          我正在研究 Go 中 goroutine 的搶占。如果您能指出文中任何錯誤并告知我,將感激不盡。

          Go1.14 版本中的搶占行為已經(jīng)發(fā)生了變化。在 Go1.14 中,goroutine 是“異步搶占”的,如發(fā)行版本所述。這意味著什么呢?

          首先,讓我們看一個簡單的例子。思考下面的 Go 程序。

          package main

          import (
              "fmt"
          )

          func main() {
              go fmt.Println("hi")
              for {
              }
          }

          在主函數(shù)中,啟動了一個只輸出 “hi” 的 goroutine。此外,存在一個無限循環(huán) for {}。

          如果我們攜帶參數(shù) GOMAXPROCS=1 運行程序時,將發(fā)生什么呢?程序似乎在輸出 “hi” 后,由于無限循環(huán)而沒有任何反應(yīng)。實際上,我使用 Go1.14 或更高版本運行該程序時(我使用 Go1.16 上運行了該程序(在 WSL2 上的 Ubuntu )),它能按照預(yù)期工作。

          有兩種方法可以阻止此程序運行。一種是使用 1.14 之前的 Go 版本運行它。另一種是運行它時攜帶參數(shù) GODEBUG=asyncpreemptoff=1。

          當(dāng)我在本地計算機上嘗試時,它的工作方式如下。

          $ GOMAXPROCS=1 GODEBUG=asyncpreemptoff=1 go run main.go
          # it blocks here

          程序沒有輸出 “hi” 。在描述為什么會發(fā)生這種情況之前,讓我先說明幾種使該程序按預(yù)期方式運行的方法。

          一種方法是在循環(huán)中添加以下代碼。



          *************** package main
          *** 2,11 ****
          --- 2,13 ----
            
            import (
                "fmt"
          +     "runtime"
            )
            
            func main() {
                go fmt.Println("hi")
                for {
          +         runtime.Gosched()
                }
            }

          runtime.Gosched() 類似于 POSIX 的 sched_yieldsched_yield 強制當(dāng)前線程放棄 CPU,以便其他線程可以運行。之所以命名為 Gosched,因為 Go 中是 goroutine,而不是線程(這是一個猜測)。換句話說,顯式調(diào)用 runtime.Gosched() 將強制對 goroutines 進行重新安排,并且我們期望將當(dāng)前運行的 goroutine 切換到另一個。

          另一種方法是使用 GOEXPERIMENT=preemptibleloops。它強制 Go 運行時在“循環(huán)”上進行搶占。這種方式不需要更改代碼。

          協(xié)作式調(diào)度 vs 搶占式調(diào)度

          首先,有兩種主要的多任務(wù)調(diào)度方法:“協(xié)作”和“搶占”。協(xié)作式多任務(wù)處理也稱為“非搶占”。在協(xié)作式多任務(wù)處理中,程序的切換方式取決于程序本身?!皡f(xié)作”一詞是指這樣一個事實:程序應(yīng)設(shè)計為可互操作的,并且它們必須彼此“協(xié)作”。在搶占式多任務(wù)處理中,程序的切換交給操作系統(tǒng)。調(diào)度是基于某種算法的,例如基于優(yōu)先級,F(xiàn)CSV,輪詢等。

          那么現(xiàn)在,goroutine 的調(diào)度是協(xié)作式還是搶占式的?至少在 Go1.13 之前,它是協(xié)作式的。

          我沒有找到任何官方文檔,但是我發(fā)現(xiàn)在以下情況會進行 goroutine 切換(并不詳盡)。

          等待讀取或?qū)懭胛淳彌_的通道 由于系統(tǒng)調(diào)用而等待 由于 time.Sleep() 而等待 等待互斥量釋放 此外,Go 會啟動一個線程,一直運行著“sysmon”函數(shù),該函數(shù)實現(xiàn)了搶占式調(diào)度(以及其他諸如使網(wǎng)絡(luò)處理的等待狀態(tài)變?yōu)榉亲枞麪顟B(tài))的功能。sysmon 運行在 M(Machine,實際上是一個系統(tǒng)線程),且不需要 P(Processor)。術(shù)語 M,P 和 G 在類似這樣的各種文章中都有解釋。我建議您在需要時參考此類文章。

          當(dāng) sysmon 發(fā)現(xiàn) M 已運行同一個 G(Goroutine)10ms 以上時,它會將該 G 的內(nèi)部參數(shù) preempt 設(shè)置為 true。然后,在函數(shù)序言中,當(dāng) G 進行函數(shù)調(diào)用時,G 會檢查自己的 preempt 標(biāo)志,如果它為 true,則它將自己與 M 分離并推入“全局隊列”?,F(xiàn)在,搶占就成功完成。順便說一下,全局隊列是與“本地隊列”不同的隊列,本地隊列是存儲 P 具有的 G。全局隊列有以下幾個作用。

          存儲那些超過本地隊列容量(256)的 G 存儲由于各種原因而等待的 G 存儲由搶占標(biāo)志分離的 G 這是 Go1.13 及其之前版本的實現(xiàn)?,F(xiàn)在,您將了解為什么上面的無限循環(huán)代碼無法按預(yù)期工作。for{} 僅僅是一個死循環(huán),所以如前所述它不會觸發(fā) goroutine 切換。您可能會想,“sysmon 是否設(shè)置了搶占標(biāo)志,因為它已經(jīng)運行了 10ms 以上?” 然而,如果沒有函數(shù)調(diào)用,即使設(shè)置了搶占標(biāo)志,也不會進行該標(biāo)志的檢查。如前所述,搶占標(biāo)志的檢查發(fā)生在函數(shù)序言中,因此不執(zhí)行任何操作的死循環(huán)不會發(fā)生搶占。

          是的,隨著 Go1.14 中引入“非協(xié)作式搶占”(異步搶占),這種行為已經(jīng)改變。

          “異步搶占”是什么意思?

          讓我們總結(jié)到目前為止的要點;Go 具有一種稱為“sysmon”的機制,可以監(jiān)視運行 10ms 以上的 goroutine 并在必要時強制搶占。但是,由于它的工作方式,在 for{} 的情況下并不會發(fā)生搶占。

          Go1.14 引入非協(xié)作式搶占,即搶占式調(diào)度,是一種使用信號的簡單有效的算法。

          首先,sysmon 仍然會檢測到運行了 10ms 以上的 G(goroutine)。然后,sysmon 向運行 G 的 P 發(fā)送信號(SIGURG)。Go 的信號處理程序會調(diào)用P上的一個叫作 gsignal 的 goroutine 來處理該信號,將其映射到 M 而不是 G,并使其檢查該信號。gsignal 看到搶占信號,停止正在運行的 G。

          由于此機制會顯式發(fā)出信號,因此無需調(diào)用函數(shù),就能將正在運行死循環(huán)的 goroutine 切換到另一個 goroutine。

          通過使用信號的異步搶占機制,上面的代碼現(xiàn)在就可以按預(yù)期工作。GODEBUG=asyncpreemptoff=1可用于禁用異步搶占。

          順便說一句,他們選擇使用 SIGURG,是因為 SIGURG 不會干擾現(xiàn)有調(diào)試器和其他信號的使用,并且因為它不在 libc 中使用。(參考)

           總結(jié)

          不執(zhí)行任何操作的無限循環(huán)不會將 CPU 傳遞給其他 goroutine,并不意味著 Go1.13 之前的機制是不好的。正如 @davecheney 所說,通常不認(rèn)為這是一個特殊問題。起初,異步搶占不是為了解決無限循環(huán)問題引出的。

          盡管異步搶占的引入使調(diào)度更具搶占性,但也有必要在 GC 期間更加謹(jǐn)慎地處理“不安全點”。在這方面對實現(xiàn)上的考慮也非常有趣。有興趣的讀者可以自己閱讀議題:非協(xié)作式 goroutine 搶占。

          參考

          • Proposal: Non-cooperative goroutine preemption 
          • runtime: non-cooperative goroutine preemption
          • runtime: tight loops should be preemptible
          • runtime: golang scheduler is not preemptive - it’s cooperative?
          • Source file src/runtime/preempt.go
          • Goroutine preemptive scheduling with new features of go 1.14
          • Go: Goroutine and Preemption
          • At which point a goroutine can yield?
          • Go: Asynchronous Preemption
          • go routine blocking the others one [duplicate]
          • (Ja) Golangのスケジューラあたりの話
          • (Ja) goroutineがスイッチされるタイミング

          NEWS

          在這次 GopherChina 2021大會上,曹春暉老師將與 Gopher 們分享 “Go 的搶占式調(diào)度”相關(guān)的精彩內(nèi)容。


          01

          主講老師:

          曹春暉(Xargin)前螞蟻金服技術(shù)專家,Go 語言 contributor 對Go語言工程化落地有多年實踐經(jīng)驗。


          Go 語言 contributor,貢獻過性能優(yōu)化的 PR且被官方采用


          出版有暢銷書 《Go 語言高級編程》


          主導(dǎo)過巨頭公司數(shù)據(jù)中臺建設(shè),開發(fā)的平臺 qps 超過 30w


          優(yōu)化過部署在幾十萬實例上的基礎(chǔ)設(shè)施軟件



          想要加入組織的 Gopher 們,請掃碼入群,即可獲得GopherChina大會的實時動向~


          點擊閱讀原文,即刻獲得早鳥票~

          瀏覽 29
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  www国产亚洲精品久久网站 | 青青草成人免费在线视频 | 四虎影库久久久 | 国产精品久久久久久久久免费挑花 | 亚洲中文视频免费 |