2 回答

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

TA貢獻1765條經驗 獲得超5個贊
這是一個普遍的多處理問題,并不特定于 goroutine 和 Go。
無法保證代碼中語句的執行順序。例如,以下序列是可能的(假設“G”是您的 goroutine,“M”是 中的代碼main
):
M:
x
定義M:
G
定義并調用男:
Sleep
叫男:
Sleep
完成男:(
Println
)x = 0
G:
x++
G:
x++
...(一些次數,甚至可能是 0)
節目結束
觀察一些交錯嘗試:
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)
}
但是,仍然沒有任何保證。要獲得任何保證,請使用任何同步技術,例如通道。
- 2 回答
- 0 關注
- 184 瀏覽
添加回答
舉報