我有以下程序:
package main
import "bytes"
import "io"
import "log"
import "os"
import "os/exec"
import "time"
func main() {
runCatFromStdinWorks(populateStdin("aaa\n"))
runCatFromStdinWorks(populateStdin("bbb\n"))
}
func populateStdin(str string) func(io.WriteCloser) {
return func(stdin io.WriteCloser) {
defer stdin.Close()
io.Copy(stdin, bytes.NewBufferString(str))
}
}
func runCatFromStdinWorks(populate_stdin_func func(io.WriteCloser)) {
cmd := exec.Command("cat")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Panic(err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Panic(err)
}
err = cmd.Start()
if err != nil {
log.Panic(err)
}
go populate_stdin_func(stdin)
go func() {
// Removing the following lines allow some output
// to be fetched from cat's stdout sometimes
time.Sleep(5 * time.Second)
io.Copy(os.Stdout, stdout)
}()
err = cmd.Wait()
if err != nil {
log.Panic(err)
}
}
在循环中运行时,我没有得到任何结果,如下所示:
$ while true; do go run cat_thingy.go; echo ; done
^C
在虚拟机中从apt安装gobu-go到ubuntu 12.04之后(转到版本go1)。我无法在Macbook Air上进行复制(转到版本go1.0.3)。这似乎是某种竞争条件。事实上,如果我进入睡眠状态(1 * time.Second),我从未在代码中以随机睡眠为代价看到问题。
我在代码中做错了什么,或者这是一个错误?如果它是一个bug,它是否已被修复?
更新:可能的线索
我发现Command.Wait将关闭与cat子进程通信的管道,即使它们仍有未读数据。我真的不确定处理它的正确方法。我想我可以创建一个通道,当写入stdin完成时通知,但我仍然需要知道cat进程是否已经结束以确保没有其他任何东西将被写入其stdout管道。我知道我可以使用cmd.Process.Wait来确定进程何时结束,但是可以安全地调用cmd.Wait吗?
更新:越来越近
这是代码的新内容。我相信这可以写到stdin并从stdout读取。如果我从处理goroutine的stdout中替换io.Copy而没有流动的话,我想我可以正确地传输数据(而不是全部缓冲)。
package main
import "bytes"
import "fmt"
import "io"
import "log"
import "os/exec"
import "runtime"
const inputBufferBlockLength = 3*64*(2<<10) // enough to be bigger than 2x the pipe buffer of 64KiB
const numInputBlocks = 6
func main() {
runtime.GOMAXPROCS(5)
runCatFromStdin(populateStdin(numInputBlocks))
}
func populateStdin(numInputBlocks int) func(io.WriteCloser, chan bool) {
return func(stdin io.WriteCloser) {
defer stdin.Close()
repeatedByteBases := []string{"a", "b", "c", "d", "e", "f"}
for i := 0; i < numInputBlocks; i++ {
repeatedBytes := bytes.NewBufferString(repeatedByteBases[i]).Bytes()
fmt.Printf("%s\n", repeatedBytes)
io.Copy(stdin, bytes.NewReader(bytes.Repeat(repeatedBytes, inputBufferBlockLength)))
}
}
}
func runCatFromStdin(populate_stdin_func func(io.WriteCloser)) {
cmd := exec.Command("cat")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Panic(err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Panic(err)
}
err = cmd.Start()
if err != nil {
log.Panic(err)
}
go populate_stdin_func(stdin)
output_done_channel := make(chan bool)
go func() {
out_bytes := new(bytes.Buffer)
io.Copy(out_bytes, stdout)
fmt.Printf("%s\n", out_bytes)
fmt.Println(out_bytes.Len())
fmt.Println(inputBufferBlockLength*numInputBlocks)
output_done_channel <- true
}()
<-output_done_channel
err = cmd.Wait()
if err != nil {
log.Panic(err)
}
}
答案 0 :(得分:4)
这是您的第一个代码的版本。请注意添加sync.WaitGroup以确保在关闭命令之前完成发送和接收转到例程。
package main
import (
"bytes"
"io"
"log"
"os"
"os/exec"
"sync"
"time"
)
func main() {
runCatFromStdinWorks(populateStdin("aaa\n"))
runCatFromStdinWorks(populateStdin("bbb\n"))
}
func populateStdin(str string) func(io.WriteCloser) {
return func(stdin io.WriteCloser) {
defer stdin.Close()
io.Copy(stdin, bytes.NewBufferString(str))
}
}
func runCatFromStdinWorks(populate_stdin_func func(io.WriteCloser)) {
cmd := exec.Command("cat")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Panic(err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Panic(err)
}
err = cmd.Start()
if err != nil {
log.Panic(err)
}
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
populate_stdin_func(stdin)
}()
go func() {
defer wg.Done()
time.Sleep(5 * time.Second)
io.Copy(os.Stdout, stdout)
}()
wg.Wait()
err = cmd.Wait()
if err != nil {
log.Panic(err)
}
}
(这只是说@peterSO所说的另一种方式; - )
答案 1 :(得分:0)
“go”语句开始执行函数或方法调用 一个独立的并发控制线程,或goroutine,在 相同的地址空间。
GoStmt =“go”表达。
表达式必须是一个电话。功能值和参数是 在调用goroutine中照常评估,但不像常规 调用,程序执行不等待调用的函数 完成。相反,该函数开始在a中独立执行 新goroutine。当函数终止时,它的goroutine也是 终止。如果函数具有任何返回值,则将其丢弃 当函数完成时。
将免费goroutine转换为函数调用。
package main
import (
"bytes"
"io"
"log"
"os"
"os/exec"
)
func main() {
runCatFromStdinWorks(populateStdin("aaa\n"))
runCatFromStdinWorks(populateStdin("bbb\n"))
}
func populateStdin(str string) func(io.WriteCloser) {
return func(stdin io.WriteCloser) {
defer stdin.Close()
io.Copy(stdin, bytes.NewBufferString(str))
}
}
func runCatFromStdinWorks(populate_stdin_func func(io.WriteCloser)) {
cmd := exec.Command("cat")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Panic(err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Panic(err)
}
err = cmd.Start()
if err != nil {
log.Panic(err)
}
populate_stdin_func(stdin)
io.Copy(os.Stdout, stdout)
err = cmd.Wait()
if err != nil {
log.Panic(err)
}
}