gpt4 book ai didi

go - 从外部命令读取错误 : fatal error all goroutines are asleep - deadlock

转载 作者:IT王子 更新时间:2023-10-29 02:20:16 27 4
gpt4 key购买 nike

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

我尝试模拟 this example .

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

主.go

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

最佳答案

尝试这样的事情(下面的解释):

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()
}

排名不分先后:

  • 与其使用 io.Pipe() 作为命令的 Stdout,不如向命令询问它是 StdoutPipe()cmd.Wait() 将确保它已为您关闭。
  • cmd.Stderr 设置为 os.Stderr 以便您可以查看 Python 程序生成的错误。
    • 我注意到只要 Python 程序写入标准错误,这个程序就会挂起。现在不是了:)
  • 不要将WaitGroup 设为全局变量;将对它的引用传递给 goroutine。
  • 与其在 goroutine 中使用 log.Fatal(),不如创建一个错误 channel 将错误传回 main()
  • 与其在 goroutine 中打印结果,不如创建一个结果 channel 将结果传回 main()
  • 确保 channel 关闭以防止阻塞/goroutine 泄漏。
  • 将 goroutine 分离到适当的函数中,使代码更易于阅读和遵循。
  • 在这个例子中,我们可以在我们的 goroutine 中创建 multipart.Reader(),因为这是我们代码中唯一使用它的部分。
  • 请注意,我正在使用 errors 包中的 Wrap() 为错误消息添加上下文。当然,这与您的问题无关,但却是一个好习惯。

for { select { ... } } 部分可能令人困惑。 This是我发现的一篇介绍这个概念的文章。基本上,select 让我们从当前可读的这两个 channel (rescerrc)中的任何一个读取,然后将每个 channel 设置为 nil 当 channel 关闭时。当两个 channel 都是 nil 时,循环退出。这让我们能够在它们进来时处理“结果或错误”。

编辑:作为 johandalabacka said on the Golang Forum ,看起来这里的主要问题是 Windows 上的 Python 在输出中添加了一个额外的 \r,问题是你的 Python 程序应该省略 \r在输出字符串或 sys.stdout.write() 而不是 print() ing。也可以在 Golang 端清理输出,但是,除了在不修改 Python 端的情况下无法正确解析之外,这个答案仍将改进程序的并发机制。

关于go - 从外部命令读取错误 : fatal error all goroutines are asleep - deadlock,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52680194/

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