golang using timeouts with channels

后端 未结 4 1492
抹茶落季
抹茶落季 2020-12-30 03:32

I am using goroutines/channels to check if list of urls are reachable. Here is my code. This seems to always return true. Why is the timeout case not getting executed? The g

4条回答
  •  情歌与酒
    2020-12-30 03:37

    check(u) will sleep in the current goroutine, i.e. the one that's running func. The select statement is only run properly once it returns, and by that time, both branches are runnable and the runtime can pick whichever one it pleases.

    You can solve it by running check inside yet another goroutine:

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

    It seems you want to check reachability of a set of URLs, and return true if one of them is available. If the timeout is long compared to the time it takes to spin up a goroutine, you could simplify this by having just one timeout for all URLs together. But we need to make sure that the channel is large enough to hold the answers from all checks, or the ones that don't "win" will block forever:

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

提交回复
热议问题