LoginSignup
1
1

More than 1 year has passed since last update.

図解 io.Pipe(Golang)

Last updated at Posted at 2022-06-10

はじめに

Goのioパッケージにはio.Readerやio.Writerのインターフェイスを有効活用するための便利なメソッドがいくつか用意されています。
自分はその中の一つであるio.Pipeの挙動を理解するのに手こずったので、備忘録として挙動や使い方を残しておこうと思います。

io.Pipeとは

io.Pipeメソッドは、io.PipeReaderとio.PipeWriterを返します。io.PipeWriterに書き込んだ内容が、io.PipeReaderから読み出せるようになります。
io.Pipeの特徴は、内部バッファを持たない点にあります。そのため、Writerに書き込んだ内容がすぐにReaderから読み出せるようになります。

モデル図はこんな感じです(2のブログから引用)。
モデル図

自分なりの理解を図にまとめると以下のようになります。

スクリーンショット 2022-06-10 11.44.39.png

サンプルコード

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についてまとめました。

参考

  1. Godoc
  2. Goのioパッケージのメソッドを図示
  3. Goならわかるシステムプログラミング 4刷
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1