ホームページ >バックエンド開発 >Golang >goroutine を使用すると Go チャネルでタイムアウトが失敗するのはなぜですか?

goroutine を使用すると Go チャネルでタイムアウトが失敗するのはなぜですか?

Mary-Kate Olsen
Mary-Kate Olsenオリジナル
2024-11-09 00:21:01798ブラウズ

Why Does Timeout Fail in Go Channels When Using Goroutines?

Go でのチャネルでのタイムアウトの使用

同時プログラミングの領域では、Goroutine とチャネルは、非同期通信パターンの実装において極めて重要な役割を果たします。ただし、タイムアウトとのやり取りを理解するのは少し難しい場合があります。

次のコード スニペットを考えてみましょう:

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 を返します。タイムアウト ケースが実行されないのはなぜですか?

この問題を理解する鍵は、ゴルーチンとチャネルがどのように相互作用するかにあります。 check(u) が外側のゴルーチンで呼び出されると、そのゴルーチンの実行が 4 秒間一時停止されます。ただし、select ステートメントは、check(u) が返されたときにのみ実行されます。その時点までに、check(u) と time.After ブランチの両方が実行できるようになります。

この問題に対処するには、独自のゴルーチン内で check(u) を分離する必要があります。

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

このバージョンでは、すべての応答を保持できるチャネルを使用します。タイムアウトは 1 秒に設定され、チャネルに到着した最初の結果が返されます。タイムアウトが経過する前にどの URL にも到達できない場合、チャネルは false 値を受け取ります。

以上がgoroutine を使用すると Go チャネルでタイムアウトが失敗するのはなぜですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。