亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

從 Go 中的標準管道() 讀取凍結

從 Go 中的標準管道() 讀取凍結

Go
守候你守候我 2022-09-12 17:00:38
我試圖從命令的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()
查看完整描述

2 回答

?
幕布斯6054654

TA貢獻1876條經驗 獲得超7個贊

此代碼中有許多爭用條件。一般來說,如果你創建一個 goroutine,應該有某種同步 -- 如 、 、 或原子。chansync.Mutexsync.WaitGroup

修復爭用條件。

  1. 呼叫之前先呼叫 。代碼不這樣做。StderrPipe()Start()

  2. 等待戈魯丁完成,然后再返回。

競爭條件可能會破壞結構...這可能意味著它泄漏了一根管道,這可以解釋為什么掛起(因為管道的寫入端沒有關閉)。exec.CmdRead()

根據經驗,請始終修復爭用條件。將它們視為高優先級 Bug。

以下是如何在沒有競爭條件的情況下編寫它的草圖:


查看完整回答
反對 回復 2022-09-12
?
qq_笑_17

TA貢獻1818條經驗 獲得超7個贊

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.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

}


查看完整回答
反對 回復 2022-09-12
  • 2 回答
  • 0 關注
  • 107 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號