亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

goroutine 在什么時候可以產生?

goroutine 在什么時候可以產生?

Go
繁星coding 2022-07-04 16:36:46
我試圖更好地理解 goroutines 在 Go 程序中是如何調度的,特別是在什么時候它們可以讓步給其他 goroutines。我們知道 goroutine 會在 syscals 上產生會阻塞它,但顯然這不是全部。這個問題引起了一些類似的擔憂,最受好評的答案說 goroutine 也可以打開函數調用,因為這樣做會調用調度程序來檢查堆棧是否需要增長,但它明確表示如果您沒有任何函數調用,只是一些數學運算,那么是的,goroutine 將鎖定線程,直到它退出或遇到可以讓其他人執行的東西。我寫了一個簡單的程序來檢查和證明:package mainimport "fmt"var output [30]string      // 3 times, 10 iterations each.var oi = 0func main() {    runtime.GOMAXPROCS(1)   // Or set it through env var GOMAXPROCS.    chanFinished1 := make(chan bool)    chanFinished2 := make(chan bool)    go loop("Goroutine 1", chanFinished1)    go loop("Goroutine 2", chanFinished2)    loop("Main", nil)    <- chanFinished1    <- chanFinished2    for _, l := range output {        fmt.Println(l)    }}func loop(name string, finished chan bool) {    for i := 0; i < 1000000000; i++ {        if i % 100000000 == 0 {            output[oi] = name            oi++        }    }    if finished != nil {        finished <- true    }}注意:我知道在數組中放置一個值并在oi不同步的情況下遞增并不完全正確,但我希望保持代碼簡單且沒有可能導致切換的東西。畢竟,可能發生的最糟糕的事情是在不推進索引(覆蓋)的情況下設置一個值,這沒什么大不了的。與這個答案不同,我避免使用作為 goroutine 啟動的函數中的任何函數調用(包括內置append())loop(),而且我GOMAXPROCS=1根據文檔明確設置:限制可以同時執行用戶級 Go 代碼的操作系統線程數。盡管如此,在輸出中我仍然看到消息Main//交錯Goroutine 1,Goroutine 2這意味著以下之一:goroutine 的執行中斷并且 goroutine 在某些時刻放棄了控制;GOMAXPROCS不像文檔中所說的那樣工作,啟動更多的操作系統線程來調度 goroutine。要么答案不完整,要么自 2016 年以來有些事情發生了變化(我在 Go 1.13.5 和 1.15.2 上進行了測試)。如果這個問題得到了回答,我很抱歉,但我既沒有找到解釋為什么這個特定的例子會產生控制,也沒有找到 goroutines 一般產生控制的點(阻塞系統調用除外)。注意:這個問題純粹是理論上的,我現在不打算解決任何實際任務,但總的來說,我假設知道 goroutine 可以產生和不能產生的點可以讓我們避免同步原語的冗余使用。
查看完整描述

2 回答

?
DIEA

TA貢獻1820條經驗 獲得超3個贊

Go 1.14 版本引入了異步搶占:

Goroutines 現在是異步可搶占的。因此,沒有函數調用的循環不再可能使調度程序死鎖或顯著延遲垃圾收集。windows/arm除、darwin/arm、js/wasm和之外的所有平臺都支持此功能plan9/*。

正如在通道是否為 goroutine 調度發送搶占點中所回答的那樣, Go 的搶占點可能會從一個版本到下一個版本發生變化。異步搶占幾乎在任何地方都增加了可能的搶占點。

您對數組的寫入output未同步,并且您的oi索引不是原子的,這意味著我們無法確定輸出數組會發生什么。當然,使用互斥體為其添加原子性會引入協作調度點。雖然這些不是協作調度切換的來源(必須根據您的輸出發生),但它們確實擾亂了我們對程序的理解。

數組保存了output字符串,使用字符串可以調用垃圾回收系統,垃圾回收系統可以使用鎖,導致調度切換。因此,這是在 Go-1.14 之前的實現中調度切換的最可能原因。


查看完整回答
反對 回復 2022-07-04
?
開滿天機

TA貢獻1786條經驗 獲得超13個贊

正如@torek 所指出的那樣,最流行的 GO 運行時環境已經使用搶占式調度幾個月了(從 1.14 開始)。否則,goroutine 可能產生的點取決于運行時環境和版本,但威廉肯尼迪給出了一個很好的總結。

我還記得幾年前在編譯器中添加了一個選項來為長時間運行的循環添加屈服點,但這是一個通常不會觸發的實驗性選項。(當然,您可以通過runtime.GoSched在循環中不時調用來手動執行此操作。)

至于你的測試,我對你在 Go 1.13.5 下運行時得到的結果感到驚訝。由于數據競爭,該行為并未完全定義(我知道您避免了任何同步機制以避免觸發產量),但我沒想到會出現這種結果。一件事是設置GOMAXPROCS為 1 意味著只有一個 goroutine 正在并發執行,但這不一定意味著當不同的 goroutine 執行時它將在同一個核心上運行。不同的核心將具有不同的緩存和(沒有同步)對 和 的值的不同output意見oi。

但是我可以建議你忘記修改全局變量,只在忙循環之前和之后記錄一條消息。這應該清楚地表明(在 GO < 1.14 中)一次只能運行一個 lopp。(多年前我曾嘗試和你做同樣的實驗,這似乎奏效了。)


查看完整回答
反對 回復 2022-07-04
  • 2 回答
  • 0 關注
  • 232 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號