gpt4 book ai didi

go - 从 Go 中的 StdoutPipe() 读取卡住

转载 作者:行者123 更新时间:2023-12-04 16:24:25 27 4
gpt4 key购买 nike

我正在尝试从命令的标准输出中读取,但每(大约)50 次它就会卡住一次。

func runProcess(process *exec.Cmd) (string, string, error) {
var stdout strings.Builder
var stderr string
process := exec.Command(programPath, params...)

go func() {
pipe, err := process.StderrPipe()
if err != nil {
return
}

buf, err := io.ReadAll(pipe)
if err != nil {
log.Warn("Error reading stderr: %v", err)
}
stderr = string(buf)
}()

pipe, err := process.StdoutPipe()
if err = process.Start(); err != nil {
return "", "", err
}

buf := make([]byte, 1024)
read, err := pipe.Read(buf) // it reads correctly from the pipe
for err == nil && read > 0 {
_, err = stdout.Write(buf[:read])
read, err = pipe.Read(buf) // this is where is stalls
}
if err = process.Wait(); err != nil {
return stdout.String(), stderr, err
}

return stdout.String(), stderr, nil
}

我尝试使用 stdout, err := io.ReadAll(pipe) 一次读取所有内容而不是读取 block ,但我得到了相同的行为。被调用的程序似乎已成功执行。它的日志文件被创建并且它是完整的。另外,当我第一次从管道(循环之前)读取时,所有输出都在那里。但是在循环内部,当第二次调用 .Read() 并且它应该返回一个 EOF(输出小于 1024 字节)时,它会卡住。

最佳答案

这段代码中有很多竞争条件。一般来说,如果你创建一个 goroutine,应该有某种同步——比如 chansync.Mutexsync.WaitGroup ,或原子。

修复竞争条件。

  1. 在调用 Start() 之前调用 StderrPipe()。代码不这样做。

  2. 等待 goroutine 完成后再返回。

竞争条件可能会破坏 exec.Cmd 结构...这可能意味着它泄漏了管道,这可以解释为什么 Read() 挂起(因为管道的写端没有关闭)。

根据经验,始终修复竞争条件。将它们视为高优先级错误。

下面是如何在没有竞争条件的情况下编写它的草图:

func runProcess(process *exec.Cmd) (stdout, stderr string, err error) {
outPipe, err := process.StdoutPipe()
if err != nil {
return "", "", err
}

// Call StderrPipe BEFORE Start().
// Easy way to do it: outside the goroutine.
errPipe, err := process.StderrPipe()
if err != nil {
return "", "", err
}

// Start process.
if err := process.Start(); err != nil {
return "", "", err
}

// Read stderr in goroutine.
var wg sync.WaitGroup
var stderrErr error
wg.Add(1)
go func() {
defer wg.Done()
data, err := ioutil.ReadAll(errPipe)
if err != nil {
stderrErr = err
} else {
stderr = string(data)
}
}()

// Read stdout in main thread.
data, stdoutErr := ioutil.ReadAll(outPipe)

// Wait until we are done reading stderr.
wg.Wait()

// Wait for process to finish.
if err := process.Wait(); err != nil {
return "", "", err
}

// Handle error from reading stdout.
if stdoutErr != nil {
return "", "", stderrErr
}
// Handle error from reading stderr.
if stderrErr != nil {
return "", "", stderrErr
}

stdout = string(data)
return stdout, stderr, nil
}

更简单的代码

所有这些都是由 os/exec 包自动完成的。 StdoutStderr 可以使用任何io.Writer,不限于*os.File

func runProcess(process *exec.Cmd) (stdout, stderr string, err error) {
var stdoutbuf, stderrbuf bytes.Buffer
process.Stdout = &stdoutbuf
process.Stderr = &stderrbuf
if err := process.Run(); err != nil {
return "", "", err
}
return stdoutbuf.String(), stderrbuf.String(), nil
}

关于go - 从 Go 中的 StdoutPipe() 读取卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68272112/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com