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

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

為什么這個 goroutine 沒有運行,即使有一個`time.Sleep`?

為什么這個 goroutine 沒有運行,即使有一個`time.Sleep`?

Go
飲歌長嘯 2022-05-23 17:29:28
拿這段代碼:func main() {    var x int    go func() {        for {            x++        }    }()    time.Sleep(time.Second)    fmt.Println("x =", x)}為什么最后x等于0?我知道 Go 的調度程序需要time.Sleep()調用來獲取 goroutine,但為什么它不這樣做呢?提示:在 for 循環放置一個time.Sleep()或調用來runtime.Gosched()但為什么?更新:檢查相同代碼的以下版本:func main() {    var x int    go func() {        for i := 0; i < 10000; i++ {            x++        }    }()    time.Sleep(time.Second)    fmt.Println("x =", x)}奇怪的是,goroutine 中的代碼現在被執行并且x不再是 0。編譯器在這里做了任何優化嗎?
查看完整描述

2 回答

?
慕桂英3389331

TA貢獻2036條經驗 獲得超8個贊

知道你在這里問什么很重要。Go 中沒有保證這個程序會做任何特別的事情,因為這個程序是無效的。但是作為對優化器的探索,提供一些關于它當前是如何實現的見解可能會很有趣。任何依賴此信息的程序都將非常脆弱和無效,但這仍然是一種好奇心。


我們可以編譯程序,然后看看輸出。我特別喜歡你給出的兩個版本,因為它們讓我們看到了差異。我已經使用 Hopper 完成了我的反編譯(這些是使用 go1.14 darwin/amd64 編譯的)。


在第二種情況下,goroutine 看起來像您認為的那樣:


void _main.main.func1(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6) {

    rax = arg6;

    for (rcx = 0x0; rcx < 0x2710; rcx = rcx + 0x1) {

            *rax = *rax + 0x1;

    }

    return;

}

這里沒有什么太令人驚訝的了。但是你好奇的第一個案例呢:


_main.main.func1:

    goto _main.main.func1;

它變成了一個noop。毫不夸張的說; 這是程序集:


                     _main.main.func1:

000000000109d1b0         nop                                                    ; CODE XREF=_main.main.func1+1

000000000109d1b1         jmp        _main.main.func1                            ; _main.main.func1

這是怎么發生的?好吧,編譯器可以看看這段代碼:


go func() {

    for {

        x++

    }

}()

它知道什么都沒有讀過x。任何東西都無法讀取x,因為沒有鎖定x,這個 goroutine 永遠不會終止。所以在這個 goroutine 完成x 后沒有什么可以讀取的。請參閱Go Memory Model了解更多關于某事在某事之前或之后發生的意義。


“但我確實讀過x!” 不,你沒有。那將是無效代碼,編譯器知道您沒有編寫無效代碼。當比賽檢測器告訴您這是無效的時,誰會這樣做?因此,由于編譯器可以清楚地看到什么都沒有讀取x,因此沒有理由費心更新它。


在您的有限循環示例中,goroutine 終止,因此之后可能會讀取某些內容x。編譯器不夠聰明,無法注意到從未進行過有效讀取,因此它沒有盡可能地優化它。也許未來的編譯器會足夠聰明,在這兩種情況下都輸出 0。也許未來的編譯器會足夠聰明,在第一種情況下完全刪除你的無操作 goroutine。


但這里的關鍵點是無限循環的情況是完全正確的,盡管效率比它可能的要低一些。非無限循環的情況也是完全正確的,盡管它的效率要低得多。


查看完整回答
反對 回復 2022-05-23
?
POPMUISE

TA貢獻1765條經驗 獲得超5個贊

這是一個普遍的多處理問題,并不特定于 goroutine 和 Go。

無法保證代碼中語句的執行順序。例如,以下序列是可能的(假設“G”是您的 goroutine,“M”是 中的代碼main):

  1. M:x定義

  2. M:G定義并調用

  3. 男:Sleep

  4. 男:Sleep完成

  5. 男:(Printlnx = 0

  6. G:x++

  7. G:x++

  8. ...(一些次數,甚至可能是 0)

  9. 節目結束

觀察一些交錯嘗試:

package main


import (

    "fmt"

    "time"

)


func main() {

    var x int


    go func() {

        for {

            time.Sleep(time.Second) 

            x++

        }

    }()

    time.Sleep(5*time.Second)

    fmt.Println("x =", x)

}

但是,仍然沒有任何保證。要獲得任何保證,請使用任何同步技術,例如通道。


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

添加回答

舉報

0/150
提交
取消
微信客服

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

幫助反饋 APP下載

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

公眾號

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