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()
}
排名不分先后:
与其使用 an
io.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 { ... } }
部分可能令人困惑。
- 1 回答
- 0 关注
- 124 浏览
添加回答
举报