【发布时间】:2020-02-19 12:37:58
【问题描述】:
我需要快速连续调用许多短期(有时是一些长期)外部进程,并实时处理stdout 和stderr。我使用StdoutPipe 和StderrPipe 找到了许多解决方案,每个都有bufio.Scanner,打包到goroutines 中。这在大多数情况下都有效,但偶尔会吞下外部命令的输出,我不知道为什么。
这是一个在 MacOS X (Mojave) 和 Linux 上显示该行为的最小示例:
package main
import (
"bufio"
"log"
"os/exec"
"sync"
)
func main() {
for i := 0; i < 50000; i++ {
log.Println("Loop")
var wg sync.WaitGroup
cmd := exec.Command("echo", "1")
stdout, err := cmd.StdoutPipe()
if err != nil {
panic(err)
}
cmd.Start()
stdoutScanner := bufio.NewScanner(stdout)
stdoutScanner.Split(bufio.ScanLines)
wg.Add(1)
go func() {
for stdoutScanner.Scan() {
line := stdoutScanner.Text()
log.Printf("[stdout] %s\n", line)
}
wg.Done()
}()
cmd.Wait()
wg.Wait()
}
}
我已经省略了 stderr 对此的处理。运行它时,我只得到大约 49,900 [stdout] 1 行(实际数量随每次运行而变化),但应该有 50,000。我看到了 50,000 条 loop 行,所以它似乎不会过早死亡。这闻起来像是某处的比赛条件,但我不知道在哪里。
如果我不将扫描循环放在 goroutine 中,它工作得很好,但是我失去了同时读取 stderr 的能力,这是我需要的。
我已尝试使用 -race 运行此程序,Go 报告没有数据竞争。
我没有想法,我做错了什么?
【问题讨论】:
标签: go