1 回答

TA貢獻1784條經驗 獲得超8個贊
一個循環只重復非阻塞檢查直到它成功會導致難以診斷的問題(至少,它會過度使用 CPU);使用阻塞檢查可以修復它。
我不太確定你的案子的細節。我寫了一個像你這樣的循環,它在 Playground 上始終掛起“進程花費太長時間”,但是當我在本地運行它時,它確實完成了。
正如我所評論的,我的目標是更簡單的設計。
Go 僅對運行中的 goroutine 具有有限的搶占權:運行中的線程僅在發生阻塞操作(如 I/O 或通道操作或等待獲取鎖)時才將控制權交給 goroutine 調度程序。
因此GOMAXPROCS=1,如果(一個)正在運行的線程開始循環,則其他任何東西都不一定有機會運行。
for { select { ...default: } }因此,A可以開始循環檢查通道中的項目,但永遠不會放棄對主線程的控制,以便另一個 goroutine 可以寫入項目。當GOMAXPROCS超過 1 時,其他代碼無論如何都會運行,但不會像 App Engine(或 Playground)上的 1 那樣運行。行為不僅取決于GOMAXPROCS,還取決于哪個 goroutine 首先運行,這不一定定義。
為了避免這種情況,刪除default:soselect是一個阻塞操作,當調度程序無法接收項目時,它會產生阻塞操作,允許其他代碼運行。您可以將其推廣到其他可能循環進行非阻塞檢查的情況;當阻塞調用不會時,它們中的任何一個都可以使資源忙于不斷地重新檢查。當GOMAXPROCS>1或運行時的有限搶占拯救您時,輪詢(如調用重復檢查)仍然可以比阻塞消耗更多的 CPU。
例如,這在 Playground 上因“過程花費太長時間”而失敗,盡管煩人的是它在我的機器上可靠地完成:
package main
import "fmt"
func main() {
c := make(chan struct{})
go func() { c <- struct{}{} }()
for {
select {
case <-c:
fmt.Println("success")
return
default:
}
}
}
我不知道是否還有其他問題,但與樣本相似的模式的掛起值得注意。
- 1 回答
- 0 關注
- 181 瀏覽
添加回答
舉報