LKY 只有原創內容的 Blog

今之能者,謂能轉貼,至於魯蛇,皆能轉貼。不原創,何以別乎?

select-case-default 忘記讓出 CPU 的地雷,只在我電腦上能跑的程式

Lin, Kao-Yuan's Avatar 2020-01-22

  1. 1. 補充:我踩到的 select-case-default 地雷

這一篇其實是為了要補充上一篇「LeetCode Concurrency Go 語言詳解:Print Zero Even Odd」沒寫到的細節,但所要解釋的概念,是針對 mulit-channel 管理上可能會犯的錯誤,與 LeetCode 比較沒關係,只是我剛好在解這一題 LeetCode 時學到的,所以最後決定將這部分獨立成一篇文章介紹。

為什麼說是地雷?當程式在自己的電腦上正常,我會很容易以為自己是對的,而且這個現象與作業系統的排程細節有關,很難找一個明確的環境原因。

補充:我踩到的 select-case-default 地雷

你可以看到,我的解題程式碼 default 那段是這麼寫的:

1
2
3
4
default:
runtime.Gosched()
//<-time.After(time.Microsecond)
i--

以上其實是被高人「指點」後的。原本是這麼寫:

1
2
default:
i--

這樣在我的 MacBook 依然正常,但是拿到 The Go Playground 上面就掛了,你可以自己修改程式碼(那篇解題文章最後有程式連結),在 The Go Playground 上試看看會怎樣?


原因是,雖然 select-case-default 會隨機均勻的嘗試每一個 case-default,但是並不會主動把 CPU 控制權交出去,需要用 runtime.Gosched() 或 <-time.After(time.Microsecond) 把 CPU 讓出給其他 goroutine。否則,其他的 goroutine 將可能沒有機會動作。

C# 裡的 Application.DoEvents() 也是一樣的意思,讓別的事件有機會被觸發。


那為什麼我的 MacBook 正常跑完?難道是 CPU 使用數量限制嗎?我們來看看這兩個平台可用的邏輯處理器數量:

  • The Go Playground: runtime.NumCPU=1
  • MacBook Air 2018: runtime.NumCPU=4

好的,我的 MacBook 果然有比較多邏輯處理器可用,但也不足以說明這就是原因。

於是,我索性在筆電上的程式碼開頭加上一行 runtime.GOMAXPROCS(1) 限制此程式與 The Go Playground 一樣,只能用一個邏輯 CPU。結果,字出來是變慢了,好像古老的打字機那樣,但也是順利正確的跑完了,無法重現 The Go Playground 上發生的錯誤。所以這樣的 bug,真的很難在不同平台上重現,是很不容易發現的地雷。

This article was last updated on days ago, and the information described in the article may have changed.