从 Go 中的标准管道() 读取冻结

我试图从命令的Stdout中读取,但每(大约)一次它冻结一次(大约)。


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

}

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


守候你守候我
浏览 114回答 2
2回答

幕布斯6054654

此代码中有许多争用条件。一般来说,如果你创建一个 goroutine,应该有某种同步 -- 如 、 、 或原子。chansync.Mutexsync.WaitGroup修复争用条件。呼叫之前先呼叫 。代码不这样做。StderrPipe()Start()等待戈鲁丁完成,然后再返回。竞争条件可能会破坏结构...这可能意味着它泄漏了一根管道,这可以解释为什么挂起(因为管道的写入端没有关闭)。exec.CmdRead()根据经验,请始终修复争用条件。将它们视为高优先级 Bug。以下是如何在没有竞争条件的情况下编写它的草图:

qq_笑_17

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}更简单的代码所有这些都是由包自动完成的。您可以使用 任何 for 和 ,您不限于 。os/execio.WriterStdoutStderr*os.Filefunc 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}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go