はじめに
Goのioパッケージにはio.Readerやio.Writerのインターフェイスを有効活用するための便利なメソッドがいくつか用意されています。
自分はその中の一つであるio.Pipe
の挙動を理解するのに手こずったので、備忘録として挙動や使い方を残しておこうと思います。
io.Pipeとは
io.Pipeメソッドは、io.PipeReaderとio.PipeWriterを返します。io.PipeWriterに書き込んだ内容が、io.PipeReaderから読み出せるようになります。
io.Pipeの特徴は、内部バッファを持たない点にあります。そのため、Writerに書き込んだ内容がすぐにReaderから読み出せるようになります。
自分なりの理解を図にまとめると以下のようになります。
サンプルコード
package main
import (
"fmt"
"io"
"log"
"os"
)
func main() {
r, w := io.Pipe()
go func() {
defer w.Close()
fmt.Fprint(w, "some io.Reader stream to be read\n")
}()
if _, err := io.Copy(os.Stdout, r); err != nil {
log.Fatal(err)
}
}
実行結果
$ go run main.go
some io.Reader stream to be read
注意点など
- io.PipeWriterにデータが書き込まれると、そのデータがio.PipeReaderからすべて読みだされるまでブロックします(参考)。そのため、書き込みを並列化するには別のgoroutineを使って並行処理する必要があります。
- これは、自分の予想ではio.Pipeが内部でバッファなしチャネルを使用しているからだと思いました。
- ただ、『Goならわかるシステムプログラミング 4刷』の4.2.1章には以下のような記述があるため、実は似ているだけで違うのかもしれません(現状わかっていません)。
io/pipe.goのコード
func Pipe() (*PipeReader, *PipeWriter) {
p := &pipe{
wrCh: make(chan []byte),
rdCh: make(chan int),
done: make(chan struct{}),
}
return &PipeReader{p}, &PipeWriter{p}
}
『[Goならわかるシステムプログラミング 4刷]の4.2.1章の記述
バッファなしのチャネルでは、受け取り側が受信をしないと送信側もブロックされ ます。3.7「io.Reader / io.Writer でストリームを自由に操る」で紹介した io.Pipeと似た動作です。バッファ付きであれば、バッファがある限りすぐに完了して次の行 が実行されます。
- 書き込みも読み込みもgoroutine safeです(1)。
おわりに
io.Pipeについてまとめました。