【问题标题】:Is launching goroutines inside of goroutines acceptable?在 goroutines 中启动 goroutines 是否可以接受?
【发布时间】:2013-04-23 16:16:39
【问题描述】:

我现在正在学习 Go,我的第一个项目是一个简单的 ping 脚本。本质上,我想 ping 一堆 url,并在每个响应时等待 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() ,但在应用程序运行一段时间后,这令人恐慌。我假设在 goroutine 被垃圾收集并且 res 不再存在之前,延迟无法调用响应上的 Close()。

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

【问题讨论】:

  • 从其他 goroutine 生成 goroutine 应该没有任何问题,但是在这种情况下有什么理由不只使用循环吗?
  • @JamesHenstridge 我没有在程序循环中执行此操作,因此我不必等待每个请求返回,然后再调用下一个请求。我正在尝试使用并发,因此每个 ping 周期都是独立的,基于它自己的延迟时间。
  • 我指的是ping() goroutine 在退出之前生成另一个 goroutine 的部分。如果你在ping() 里面放一个循环,你会得到同样的效果。
  • 这听起来像是一个更好的解决方案。我会更新我的代码。

标签: go goroutine


【解决方案1】:

没关系。从另一个 goroutine 调用一个 goroutine 是完全可以接受的。调用的 goroutine 仍然会退出,新的 goroutine 会继续愉快的进行。

【讨论】:

    【解决方案2】:

    从一个 goroutine 中扩展一个新的 goroutine 本身就很好。

    但我怀疑这是解决您的问题的最简单和最干净的解决方案。 我猜你的第一个版本做了显而易见的事情,并在一个无限循环中对每个 URL 进行 ping 操作。并且这会影响延迟:一旦函数返回,就会执行延迟调用。 (这与 goroutine 蜂鸣“垃圾收集无关;实际上 goroutines 刚刚结束,没有被收集)。在一个无限循环中,你永远不会返回,你只是累积永远不会执行的延迟调用。因此你永远不会关闭所有打开的res.Body 并且您的内存不足/无论如何都会出现恐慌。

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

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

    【讨论】:

    • 如果我不调用 res.Body.Close() 内存使用量也会增加,还是会丢弃 res?
    • 必须调用 res.Body.Close。由于某种原因,它被记录在案。 (否则您的代码会泄漏。)
    • 如果我不像上面那样把它变成一个 var 怎么办? _, err := http.Get(url) 会泄漏吗?
    • 不知道。试试看:-)
    猜你喜欢
    • 2021-12-19
    • 1970-01-01
    • 2012-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-27
    • 2014-10-22
    • 1970-01-01
    相关资源
    最近更新 更多