首頁 >後端開發 >Golang >為什麼 Go Channel 使用 Goroutine 會逾時失敗?

為什麼 Go Channel 使用 Goroutine 會逾時失敗?

Mary-Kate Olsen
Mary-Kate Olsen原創
2024-11-09 00:21:01808瀏覽

Why Does Timeout Fail in Go Channels When Using Goroutines?

在 Go 中使用超時和通道

在並發程式設計領域,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中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn