golang 中把 io.TeeReader
和 io.Pipe
结合使用
这篇文章主要解释和展示怎么在golang当中如何把 io.TeeReader()
and io.Pipe
结合使用。
你可以轻松地根据官方样例来使用bytes.Buffer
和io.TeeReader()
。
但是,如果你去使用io.Pipe
的时候你会遇到一些问题。所有有了这篇文章。
注意:这篇文章主要是面向初学者,而不是经验丰富的Golang使用者。
Table of Contents
初始修改
作为一个初学者,你可能轻松想到以下的修改方案。
func main() {
r := strings.NewReader("some io.Reader stream to be read\n")
pr, pw := io.Pipe()
tee := io.TeeReader(r, pw)
// create channel to synchronize
done := make(chan bool)
defer close(done)
printall := func(r io.Reader) {
b, err := ioutil.ReadAll(r)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v: %s", reflect.TypeOf(r), b)
}
defer pw.Close()
printall(tee)
printall(pr)
}
如果你运行这段代码,你会得到如下的错误信息:
fatal error: all goroutines are asleep - deadlock!
这个错误来愿意你尝试在一个相同的线程中去做这样的事,这个就会在你的脑袋上敲出一个panic。
修复Bug!
你可以轻松地通过一些微小的修改和增加同步来修复它。
func main() {
r := strings.NewReader("some io.Reader stream to be read\n")
pr, pw := io.Pipe()
tee := io.TeeReader(r, pw)
// create channel to synchronize
done := make(chan bool)
defer close(done)
printall := func(r io.Reader) {
b, err := ioutil.ReadAll(r)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v: %s", reflect.TypeOf(r), b)
}
go func() {
printall(pr)
done <- true
}()
go func() {
defer pw.Close()
printall(tee)
done <- true
}()
// wait until both are done
for c := 0; c < 2; c++ {
<-done
}
}
done
chan 被添加以此来确保同步能够正确地发生,即在main线程退出之前。pw.Close()
必须要和printall(tr)
放在另一个goroutine内部。这是因为TeeReader
会返回一个将 r 中的数据读出后同时写入 w 的Reader中。- 由于协程的存在,你现在可以更改两个独立的
go func(){}
的次序,不像官方例子io.TeeReader()
提供的那样。
希望这篇文章对你有用。
The End