Heim  >  Artikel  >  Backend-Entwicklung  >  Warum schlägt Timeout in Go-Kanälen fehl, wenn Goroutinen verwendet werden?

Warum schlägt Timeout in Go-Kanälen fehl, wenn Goroutinen verwendet werden?

Mary-Kate Olsen
Mary-Kate OlsenOriginal
2024-11-09 00:21:01751Durchsuche

Why Does Timeout Fail in Go Channels When Using Goroutines?

Timeouts mit Kanälen in Go verwenden

Im Bereich der gleichzeitigen Programmierung spielen Goroutinen und Kanäle eine zentrale Rolle bei der Implementierung asynchroner Kommunikationsmuster. Es kann jedoch etwas schwierig sein, zu verstehen, wie sie mit Zeitüberschreitungen interagieren.

Bedenken Sie den folgenden Codeausschnitt:

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"}))
}

Das Ziel dieses Codes besteht darin, festzustellen, ob eine Liste von URLs erreichbar ist . Es wird jedoch immer true zurückgegeben, unabhängig davon, ob eine URL nicht erreichbar ist. Warum wird der Timeout-Fall nicht ausgeführt?

Der Schlüssel zum Verständnis dieses Problems liegt in der Interaktion von Goroutinen und Kanälen. Wenn check(u) in der äußeren Goroutine aufgerufen wird, wird die Ausführung dieser Goroutine für 4 Sekunden angehalten. Die Select-Anweisung wird jedoch erst ausgeführt, wenn check(u) zurückkehrt. Zu diesem Zeitpunkt sind sowohl check(u) als auch time.After-Zweige betriebsbereit.

Um dieses Problem zu beheben, müssen wir check(u) innerhalb seiner eigenen Goroutine isolieren:

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"}))
}

In diesem überarbeiteten Code wird check(u) in einer separaten Goroutine aufgerufen. Dadurch kann die Select-Anweisung richtig zwischen dem Abschluss von check(u) und der Timeout-Bedingung unterscheiden.

Alternativ könnten wir den Code vereinfachen, indem wir ein einziges Timeout für alle URLs verwenden:

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"}))
}

In dieser Version verwenden wir einen Kanal, der alle Antworten speichern kann. Ein Timeout wird auf eine Sekunde festgelegt und das erste Ergebnis, das im Kanal ankommt, wird zurückgegeben. Wenn keine der URLs vor Ablauf des Timeouts erreichbar ist, erhält der Kanal einen falschen Wert.

Das obige ist der detaillierte Inhalt vonWarum schlägt Timeout in Go-Kanälen fehl, wenn Goroutinen verwendet werden?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn