這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
轉自:http://blog.dccmx.com/2012/03/small-problem-about-goroutine/
goroutine是Go語言的標誌性特性之一。配合channel,形成了Go語言處理並發的基礎。但是,目前還有些小問題,或者說小不爽。就是會給你造成真並行的假象。
看下面的例子:
package main import ( "time" "runtime") func main () { ch := make(chan int) go func(ch chan int) { time.Sleep(1 * 1e9) ch <- 1 }(ch) go func(ch chan int) { for { select { case <-ch: println("got!!!") return default: println("waiting...") } } }(ch) time.Sleep(2 * 1e9)}
這裡,我們的意圖很明顯,用一個goroutine做定時器,時間到了向channel發一訊號,再用一個goroutine做事,收到訊號後停止,最後一行sleep是為了防止main函數退出導致其他goroutine退出(為了省代碼,這裡不用channel同步了)。看起來天衣無縫,很是完美,驚歎一下Go的簡潔優美。但是,運行之後會發現,一直輸出waiting…,永遠不會退出!
為什嗎??
因為Go語言現在的實現還不是很成熟,預設情況下,同時只有1個goroutine在跑,而當這個goroutine阻塞的時候,才會調度到其他的goroutine去,就像協程,不過這裡由Go語言運行時幫你調度。
這個例子有三個解決方案。
1.在main函數裡面第一行調用runtime.GOMAXPROCS(2)函數,將同時goroutine數設為2(或者更大),這個解決方案至少在我的雙核機上跑是沒問題的。1秒後就輸出got!!!然後結束了。
2.製造阻塞,比如在println(“waiting…”)後面調用time.Sleep(1 * 1e8),這樣輸出10個waiting…後就got了。
3.手動切換,在println(“waiting…”)後面調用runtime.Gosched(),手動切換goroutine,這樣也能結束。
這個問題很典型,初學者經常會中招,雖然從語義上講,原始的做法並沒有錯,但是由於Go運行時還不夠成熟(至少Go1看來是不打算解決了,以後會取消GOMAXPROCS改為自動判斷),總之,大家小心點就好。