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,真的很難在不同平台上重現,是很不容易發現的地雷。

本文最后更新于 天前,文中所描述的信息可能已发生改变