同時プログラミングの領域では、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 サイトの他の関連記事を参照してください。