在並發程式設計領域,Goroutines 和通道在實現非同步通訊模式中發揮關鍵作用。然而,理解它們如何與超時互動可能有點棘手。
考慮以下程式碼片段:
import "fmt" import "time" func check(u string) bool { time.Sleep(4 * time.Second) return true } func IsReachable(urls []string) bool { ch := make(chan bool, 1) for _, url := range urls { go func(u string) { select { case ch <- check(u): case <-time.After(time.Second): ch <- false } }(url) } return <-ch } func main() { fmt.Println(IsReachable([]string{"url1"})) }
此程式碼的目標是確定 URL 清單是否可存取。但是,無論是否有任何 URL 無法訪問,它始終會傳回 true。為什麼超時情況沒有被執行?
理解這個問題的關鍵在於 Goroutine 和 Channels 是如何互動的。當在外部 Goroutine 中呼叫 check(u) 時,它會暫停該 Goroutine 的執行 4 秒。但是,select 語句僅在 check(u) 傳回後執行。到那時, check(u) 和 time.After 分支都已準備好運行。
為了解決這個問題,我們需要將check(u) 隔離在它自己的Goroutine 中:
import "fmt" import "time" func check(u string, checked chan<- bool) { time.Sleep(4 * time.Second) checked <- true } func IsReachable(urls []string) bool { ch := make(chan bool, 1) for _, url := range urls { go func(u string) { checked := make(chan bool) go check(u, checked) select { case ret := <-checked: ch <- ret case <-time.After(1 * time.Second): ch <- false } }(url) } return <-ch } func main() { fmt.Println(IsReachable([]string{"url1"})) }
在此修改後的程式碼中,check(u) 在單獨的Goroutine 中調用。這允許 select 語句正確區分 check(u) 的完成和逾時條件。
或者,我們可以透過對所有 URL 使用單一超時來簡化程式碼:
import "fmt" import "time" func check(u string, ch chan<- bool) { time.Sleep(4 * time.Second) ch <- true } func IsReachable(urls []string) bool { ch := make(chan bool, len(urls)) for _, url := range urls { go check(url, ch) } time.AfterFunc(time.Second, func() { ch <- false }) return <-ch } func main() { fmt.Println(IsReachable([]string{"url1", "url2"})) }
在此版本中,我們使用一個可以保存所有回應的通道。設定超時時間為一秒,返回通道中第一個到達的結果。如果在逾時到期之前無法存取任何 URL,則通道將收到 false 值。
以上是為什麼 Go Channel 使用 Goroutine 會逾時失敗?的詳細內容。更多資訊請關注PHP中文網其他相關文章!