如果在不指定GOMAXPROCS環境變量的情況下運行Go程序,則會定期在單個OS線程中執行Go例程。但是,爲了讓程序看起來像是多線程的(這就是goroutines的用途,不是嗎?),Go調度器有時必須切換執行上下文,因此每個goroutine都可以完成它的工作。
正如我所說的,當沒有指定GOMAXPROCS變量時,Go運行時只允許使用一個線程,所以當goroutine執行一些常規工作(如計算或IO)時切換執行上下文是不可能的到普通的C函數)。只有在使用Go併發原語時才能切換上下文。當你打開幾個chans,或者(這是你的情況),當你明確地告訴調度器切換上下文 - 這是runtime.Gosched
的用途。
因此,簡而言之,當一個例程中的執行上下文達到Gosched
調用時,指示調度程序將執行切換到另一個goroutine。在你的情況下,有兩個goroutines,main(它代表程序的'main'線程)和另外一個,你用go say
創建的。如果刪除Gosched
調用,執行上下文將永遠不會從第一個goroutine傳輸到第2個goroutine,因此對您而言沒有「世界」。當存在Gosched
時,調度程序會將每次循環迭代的執行從第一個goroutine轉移到第二個goroutine,反之亦然,所以您有'hello'和'world'交錯。我們稱之爲'合作式多任務':goroutines必須明確地將控制權交給其他goroutines。大多數現代操作系統使用的方法稱爲「搶先式多任務」:執行線程不關心控制傳輸;調度程序將執行上下文透明地切換給它們。經常使用合作的方法來實現'綠色線程',也就是邏輯併發協程,它們不會將1:1映射到操作系統線程 - 這就是Go運行時及其goroutines的實現方式。
更新
我已經提到GOMAXPROCS環境變量,但沒有說明它是什麼。現在是解決這個問題的時候了。
當此變量設置爲正數N
時,Go運行時將能夠創建最多N
本機線程,其上將調度所有綠色線程。本機線程是由操作系統創建的一種線程(Windows線程,pthreads等)。這意味着如果N
大於1,那麼goroutines可能會安排在不同的本地線程中執行,因此可以並行運行(至少取決於您的計算機功能:如果您的系統基於多核處理器,這些線程可能會真正並行;如果您的處理器具有單核,那麼在OS線程中實現的搶先式多任務將創建並行執行的可見性)。
可以使用runtime.GOMAXPROCS()
函數來設置GOMAXPROCS變量,而不是預先設置環境變量。使用這樣的事情在你的程序,而不是當前main
的:
func main() {
runtime.GOMAXPROCS(2)
go say("world")
say("hello")
}
在這種情況下,你可以看到有趣的結果。您可能會得到打印錯誤交錯的「hello」和「world」行,例如
hello
hello
world
hello
world
world
...
如果夠程定於單獨操作系統的線程會發生這種情況。實際上,搶先式多任務處理(或多核系統情況下的並行處理)如何實現:線程是並行的,並且它們的組合輸出是不確定的。順便說一句,你可以離開或刪除Gosched
通話,它似乎沒有任何影響時,GOMAXPROCS是大於1.
以下是我所得到的與runtime.GOMAXPROCS
調用的幾個運行的程序。
hyperplex /tmp % go run test.go
hello
hello
hello
world
hello
world
hello
world
hyperplex /tmp % go run test.go
hello
world
hello
world
hello
world
hello
world
hello
world
hyperplex /tmp % go run test.go
hello
hello
hello
hello
hello
hyperplex /tmp % go run test.go
hello
world
hello
world
hello
world
hello
world
hello
world
請參閱,有時輸出很漂亮,有時不是。非決定論在行動:)
另一個更新
看起來像在圍棋編譯器Go運行時力的新版本夠程產生不僅併發原語的用法,但對OS的系統調用了。這意味着執行上下文可以在IO函數調用中在goroutine之間切換。因此,在最近的Go編譯器中,即使當GOMAXPROCS未設置或設置爲1時,也可以觀察到不確定行爲。
偉大的工作!但我沒有遇到這個問題下去1.0.3,更奇怪。 – MrROY
這是真的。我剛剛檢查了1.0.3版本,是的,這種行爲沒有出現:即使使用GOMAXPROCS == 1,程序的工作方式就好像GOMAXPROCS> = 2。似乎在1.0.3中調度器已經調整。 –
謝謝!我希望我能兩次讚揚這個! –