是否可以接受goroutines中的goroutines?

时间:2013-04-29 20:58:31

标签: go goroutine

我正在学习Go现在正在使用,我的第一个项目之一就是一个简单的ping脚本。基本上我想ping一堆网址,并在每个网站的响应中等待XXX秒,然后再次ping。这是删节代码:

func main() {
    //  read our text file of urls
    f, err := ioutil.ReadFile(urlFile)
    if err != nil {
        log.Print(err)
    }

    urlStrings := []string{}
    urlStrings = strings.Split(string(f), "\n")

    for _, v := range urlStrings {
        go ping(v)
    }

    //  output logs to the terminal
    //  channel is global
    for i := range c {
        fmt.Println(i)
    }
}

func ping(url string) {
    //  for our lag timer
    start := time.Now()

    //  make our request
    _, err := http.Get(url)

    if err != nil {
        msg := url + " Error:" + err.Error()

        fmt.Println(msg)

        c <- msg
        reportError(msg)
    } else {
        lag := time.Since(start)
        var msg string

        //  running slow
        if lag > lagThreshold*time.Second {
            msg = url + " lag: " + lag.String()
            reportError(msg)
        }

        msg = url + ", lag: " + lag.String()
        c <- msg
    }

    time.Sleep(pingInterval * time.Second)
    go ping(url) // is this acceptable?
}

在我的Get请求中,我之前调用了defer res.Body.Close(),但是在应用程序运行一段时间之后就出现了问题。我假设defer不能在响应上调用Close(),直到goroutine被垃圾收集并且res不再存在。

这让我想到如果通过在goroutine中调用goroutine是最佳实践,或者如果我导致函数永远不会退出,那么只有在goroutine被垃圾收集后才会调用defer。

2 个答案:

答案 0 :(得分:14)

没关系。从另一个goroutine打电话给goroutine是完全可以接受的。调用goroutine仍然会退出,而新的goroutine将继续它的快乐方式。

答案 1 :(得分:7)

从goroutine中掏出新的goroutine本身就很好。

但我怀疑这是解决您问题的最简单,最干净的解决方案。 我猜你的第一个版本做了显而易见的事情并在无限循环中ping每个URL。这咬人推迟:一旦函数返回,就会执行延迟调用。 (这与goroutine beeing“垃圾收集无关;实际上goroutines刚刚结束,没有收集)。在无限循环中你永远不会返回,你只是累积从未执行的deferred调用。因此你永远不会关闭所有开放res.Body和你的内存/任何事情都会耗尽并且看到恐慌。

defer res.Body.Close是一个很好的习惯用法,但不是在无限循环中。

我会尝试你的第一个版本并直接在nil错误路径上执行res.Body.Close。