【问题标题】:Leaking goroutine when a non-blocking readline hangs当非阻塞读取行挂起时泄漏 goroutine
【发布时间】:2015-09-21 09:40:50
【问题描述】:

假设你有这样的结构:

ch := make(chan string)
errCh := make(chan error)
go func() {
    line, _, err := bufio.NewReader(r).ReadLine()
    if err != nil {
        errCh <- err
    } else {
        ch <- string(line)
    }
}()
select {
case err := <-errCh:
    return "", err
case line := <-ch:
    return line, nil
case <-time.After(5 * time.Second):
    return "", TimeoutError
}

在 5 秒超时的情况下,goroutine 挂起,直到 ReadLine 返回,这可能永远不会发生。我的项目是一个长期运行的服务器,所以我不希望堆积卡住的 goroutine。

【问题讨论】:

  • 从变量r(互联网、磁盘、集群)中读取什么?
  • 为什么你认为ReadLine 可能永远不会回来?你从哪里读到没有超时程序?
  • 我正在阅读来自 exec.Command.StdoutPipe() 的 r
  • 您是否期望 exec 调用中的执行挂起或输出为空?在该 goroutine 中使用 exec.Command.Output() 将收集输出并在 exec.Command 没有执行而不阻塞程序其余部分的情况下引发错误。
  • .Output() 运行一个程序并等待它退出,但我不想等待程序退出。主程序正在启动另一个进程。此过程应在准备好后将一行打印到标准输出。

标签: go goroutine


【解决方案1】:

在进程退出或方法读取一行之前,ReadLine 不会返回。管道没有最后期限或超时机制。

如果对 ReadLine 的调用在超时后返回,goroutine 将阻塞。这可以通过使用缓冲通道来解决:

ch := make(chan string, 1)
errCh := make(chan error, 1)

应用程序应调用Wait 来清理与该命令关联的资源。 goroutine 是一个调用它的好地方:

go func() {
  line, _, err := bufio.NewReader(r).ReadLine()
  if err != nil {
    errCh <- err
  } else {
    ch <- string(line)
  }
  cmd.Wait() // <-- add this line
}()

这将导致 goroutine 阻塞,而这正是您试图避免的。另一种方法是应用程序为每个命令泄漏资源。

【讨论】:

  • 这解决了我的问题。我正在尝试各种方法来关闭阅读器,终止进程等。当它尝试写入 errCh 时,我没有意识到 goroutine 正在阻塞
  • 附言。我可以添加cmd.Release() 而不是Wait() 吗?
  • @Gattster 什么是 cmd.Release()?
  • 对不起,我的意思是cmd.Process.Release()golang.org/pkg/os/#Process.Release
  • Process.Release 不会清理 Command 使用的资源。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-17
  • 1970-01-01
  • 2022-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多