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

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

從外部命令讀取錯誤:致命錯誤所有 goroutines 都睡著了 - 死鎖

從外部命令讀取錯誤:致命錯誤所有 goroutines 都睡著了 - 死鎖

Go
慕碼人8056858 2023-04-17 16:37:09
我想用 Python 將 mime/multipart 消息寫入標準輸出,并使用mime/multipart包在 Golang 中讀取該消息。這只是一個學習練習。我試著模擬這個例子。output.py#!/usr/bin/env python2.7import syss = "--foo\r\nFoo: one\r\n\r\nA section\r\n" +"--foo\r\nFoo: two\r\n\r\nAnd another\r\n" +"--foo--\r\n"print s 主程序package mainimport (    "io"    "os/exec"    "mime/multipart"    "log"    "io/ioutil"    "fmt"    "sync")var wg sync.WaitGroupfunc main() {    pr,pw := io.Pipe()    defer pw.Close()    cmd := exec.Command("python","output.py")    cmd.Stdout = pw    mr := multipart.NewReader(pr,"foo")    wg.Add(1)    go func() {        defer wg.Done()        for {            p, err := mr.NextPart()            if err == io.EOF {                fmt.Println("EOF")                return            }            if err != nil {                log.Fatal(err)            }            slurp, err := ioutil.ReadAll(p)            if err != nil {                log.Fatal(err)            }            fmt.Printf("Part : %q\n", slurp)            return        }    }()    if err := cmd.Start(); err != nil {        log.Fatal(err)    }    cmd.Wait()    wg.Wait()}輸出go run main.go:fatal error: all goroutines are asleep - deadlock!StackOverflow 上有關此主題的其他答案與未關閉的頻道有關,但我什至沒有使用頻道。我知道某個地方有無限循環或類似的東西,但我沒有看到。
查看完整描述

1 回答

?
慕沐林林

TA貢獻2016條經驗 獲得超9個贊

嘗試這樣的事情(下面的解釋):


package main


import (

? ? "fmt"

? ? "io"

? ? "io/ioutil"

? ? "log"

? ? "mime/multipart"

? ? "os"

? ? "os/exec"

? ? "sync"


? ? "github.com/pkg/errors"

)


func readCommand(cmdStdout io.ReadCloser, wg *sync.WaitGroup, resc chan<- []byte, errc chan<- error) {

? ? defer wg.Done()

? ? defer close(errc)

? ? defer close(resc)


? ? mr := multipart.NewReader(cmdStdout, "foo")


? ? for {

? ? ? ? part, err := mr.NextPart()

? ? ? ? if err != nil {

? ? ? ? ? ? if err == io.EOF {

? ? ? ? ? ? ? ? fmt.Println("EOF")

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? errc <- errors.Wrap(err, "failed to get next part")

? ? ? ? ? ? }


? ? ? ? ? ? return

? ? ? ? }


? ? ? ? slurp, err := ioutil.ReadAll(part)

? ? ? ? if err != nil {

? ? ? ? ? ? errc <- errors.Wrap(err, "failed to read part")

? ? ? ? ? ? return

? ? ? ? }


? ? ? ? resc <- slurp

? ? }

}


func main() {

? ? cmd := exec.Command("python", "output.py")

? ? cmd.Stderr = os.Stderr

? ? pr, err := cmd.StdoutPipe()

? ? if err != nil {

? ? ? ? log.Fatal(err)

? ? }


? ? var wg sync.WaitGroup

? ? wg.Add(1)


? ? resc := make(chan []byte)

? ? errc := make(chan error)

? ? go readCommand(pr, &wg, resc, errc)


? ? if err := cmd.Start(); err != nil {

? ? ? ? log.Fatal(err)

? ? }


? ? for {

? ? ? ? select {

? ? ? ? case err, ok := <-errc:

? ? ? ? ? ? if !ok {

? ? ? ? ? ? ? ? errc = nil

? ? ? ? ? ? ? ? break

? ? ? ? ? ? }


? ? ? ? ? ? if err != nil {

? ? ? ? ? ? ? ? log.Fatal(errors.Wrap(err, "error from goroutine"))

? ? ? ? ? ? }


? ? ? ? case res, ok := <-resc:

? ? ? ? ? ? if !ok {

? ? ? ? ? ? ? ? resc = nil

? ? ? ? ? ? ? ? break

? ? ? ? ? ? }


? ? ? ? ? ? fmt.Printf("Part from goroutine: %q\n", res)

? ? ? ? }


? ? ? ? if errc == nil && resc == nil {

? ? ? ? ? ? break

? ? ? ? }

? ? }


? ? cmd.Wait()

? ? wg.Wait()

}

排名不分先后:

  • 與其使用 anio.Pipe()作為命令的Stdout,不如向命令詢問它的StdoutPipe()。cmd.Wait()將確保它為您關閉。

  • 設置cmd.Stderros.Stderr以便您可以查看 Python 程序生成的錯誤。

    • 我注意到只要 Python 程序寫入標準錯誤,這個程序就會掛起。現在它沒有:)

  • 不要將其設為WaitGroup全局變量;將對它的引用傳遞給 goroutine。

  • 與其log.Fatal()在 goroutine 內部執行 ing,不如創建一個錯誤通道來將錯誤傳回給main().

  • 與其在 goroutine 中打印結果,不如創建一個結果通道將結果傳回給main().

  • 確保通道關閉以防止阻塞/goroutine 泄漏。

  • 將 goroutine 分離到一個適當的函數中,使代碼更易于閱讀和遵循。

  • 在這個例子中,我們可以在我們的 goroutine 內部創建multipart.Reader(),因為這是我們代碼中唯一使用它的部分。

  • 請注意,我使用Wrap()fromerrors包來為錯誤消息添加上下文。當然,這與您的問題無關,但這是一個好習慣。

for { select { ... } }部分可能令人困惑。


查看完整回答
反對 回復 2023-04-17
  • 1 回答
  • 0 關注
  • 173 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

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

幫助反饋 APP下載

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

公眾號

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