从外部命令读取错误:致命错误所有 goroutines 都睡着了 - 死锁

我想用 Python 将 mime/multipart 消息写入标准输出,并使用mime/multipart包在 Golang 中读取该消息。这只是一个学习练习。

我试着模拟这个例子

output.py

#!/usr/bin/env python2.7

import sys

s = "--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 main


import (

    "io"

    "os/exec"

    "mime/multipart"

    "log"

    "io/ioutil"

    "fmt"

    "sync"

)


var wg sync.WaitGroup


func 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 上有关此主题的其他答案与未关闭的频道有关,但我什至没有使用频道。我知道某个地方有无限循环或类似的东西,但我没有看到。


慕码人8056858
浏览 125回答 1
1回答

慕沐林林

尝试这样的事情(下面的解释):package mainimport (    "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.Stderr为os.Stderr以便您可以查看 Python 程序生成的错误。我注意到只要 Python 程序写入标准错误,这个程序就会挂起。现在它没有:)不要将其设为WaitGroup全局变量;将对它的引用传递给 goroutine。与其log.Fatal()在 goroutine 内部执行 ing,不如创建一个错误通道来将错误传回给main().与其在 goroutine 中打印结果,不如创建一个结果通道将结果传回给main().确保通道关闭以防止阻塞/goroutine 泄漏。将 goroutine 分离到一个适当的函数中,使代码更易于阅读和遵循。在这个例子中,我们可以在我们的 goroutine 内部创建multipart.Reader(),因为这是我们代码中唯一使用它的部分。请注意,我使用Wrap()fromerrors包来为错误消息添加上下文。当然,这与您的问题无关,但这是一个好习惯。该for { select { ... } }部分可能令人困惑。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go