当非阻塞的读取行操作 hang 时,会出现 goroutine 泄漏的问题。

huangapple go评论79阅读模式
英文:

Leaking goroutine when a non-blocking readline hangs

问题

假设你有这样的结构:

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。

英文:

Assuming you have a structure like this:

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

In the case of the 5 second timeout, the goroutine hangs until ReadLine returns, which may never happen. My project is a long-running server, so I don't want a buildup of stuck goroutines.

答案1

得分: 3

ReadLine方法在进程退出或读取到一行数据之前不会返回。管道没有截止时间或超时机制。

如果ReadLine方法在超时后返回,goroutine将会阻塞。可以通过使用带缓冲的通道来解决这个问题:

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

应用程序应该调用Wait方法来清理与命令相关的资源。在goroutine中调用Wait方法是一个好的选择:

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

这将导致goroutine阻塞,这正是你想要避免的。另一种情况是应用程序会为每个命令泄漏资源。

英文:

ReadLine will not return until either the process exits or the method reads a line. There's no deadline or timeout mechanism for pipes.

The goroutine will block if the call to ReadLine returns after the timeout. This can be fixed by using buffered channels:

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

The application should call Wait to cleanup resources associated with the command. The goroutine is a good place to call it:

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

This will cause the goroutine to block, the very thing you are trying to avoid. The alternative is that the application leaks resources for each command.

huangapple
  • 本文由 发表于 2015年9月21日 07:42:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/32685442.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定