io.Readerはスレッドセーフなのか

Golangインターフェイスにある io.Reader は、スレッドセーフなのか。気になったので、調べてみた。
結論から言えば

  • os.File は、スレッドセーフになってる
  • それ以外は、実装によるけど、スレッドセーフじゃないと思った方がいい

っという感じになってた。

os.File

今回は Windows 版の実装を見てみる。

https://github.com/golang/go/blob/release-branch.go1.11/src/internal/poll/fd_windows.go#L465

os.File.Read() は、内部で poll.FD.read() を呼び出している。

さて、この poll.FD の実装を見てみると、独自の fdMutex でロックをかけている。
ということで、マルチスレッドで使っても大丈夫っぽい。

func (fd *FD) Read(buf []byte) (int, error) {
    if err := fd.readLock(); err != nil {
        return 0, err
    }
    defer fd.readUnlock()

    // 以下、読み込み処理
}

bytes.Reader

io.Reader の実装例として bytes.Reader を見てみる。

https://github.com/golang/go/blob/release-branch.go1.11/src/bytes/reader.go#L39

見る限り、Mutexとかでロックをかけていない。
ということで、マルチスレッドだとちゃんと読み込めない場合がある。

func (r *Reader) Read(b []byte) (n int, err error) {
    if r.i >= int64(len(r.s)) {
        return 0, io.EOF
    }
    r.prevRune = -1
    n = copy(b, r.s[r.i:])
    r.i += int64(n)
    return
}

結論

io.Reader を複数スレッドで使うということ自体が無いとは思われるけど。
複数スレッドで使う場合は、独自にロック処理をかけること忘れないようにしよう。