首页  >  文章  >  后端开发  >  如何在带通道的 Go Goroutine 中正确实现超时?

如何在带通道的 Go Goroutine 中正确实现超时?

DDD
DDD原创
2024-11-08 14:57:02258浏览

How to Implement Timeouts Correctly in Go Goroutines with Channels?

在 Go 中使用超时和通道

Goroutines 和通道为 Go 中的并发编程提供了强大的机制。然而,处理 goroutine 中的超时可能会很棘手。

在您想要使用 goroutine 和超时检查 URL 列表的可达性的场景中,可能存在超时不会被执行的情况,即使一些 URL 无法访问。

让我们分析一下提供的代码:

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
}

问题出在 check 函数中。当你在 goroutine 中使用 time.Sleep 时,它会暂停当前的 goroutine,在本例中是运行 check 函数的 goroutine。当 check 函数暂停时,外层 goroutine 中的 select 语句仍会尝试运行。

在这种情况下,select 语句的两个分支(检查结果或超时)将在 4 年后运行检查返回时的秒数。然而,由于两个分支都是可运行的,运行时可以选择执行其中一个,这可能会导致始终返回 true。

要解决这个问题,您需要为每个检查函数创建一个新的 goroutine,如下所示更正后的代码如下:

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(time.Second):
                ch <- false
            }
        }(url)
    }
    return <-ch
}

这种情况下,check函数是在一个单独的goroutine中执行的,确保它不会暂停外层goroutine,并且超时可以正确执行。

以上是如何在带通道的 Go Goroutine 中正确实现超时?的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn