channel の close と range による受信ループ

channel には「もうこれ以上送信しない」ことを伝える仕組みがあります。close です。close された channel に対して range を使うと、すべてのデータを受信し終わるまでループできます。

channel を close する

close 関数で channel を閉じます。

ch := make(chan int)
close(ch)

close された channel からは、もう送信できません。送信しようとすると panic になります。

一方、受信は可能です。close 前に送信されたデータがあればそれを受信し、なければゼロ値が返ります。

close されたかどうかを確認する

受信時に 2 つ目の戻り値で、channel が開いているかどうかを確認できます。

v, ok := <-ch
if !ok {
    fmt.Println("channel は閉じられています")
}

okfalse なら、channel は close されていて、もうデータは来ません。

range で受信ループ

for range を使うと、channel が close されるまで受信し続けることができます。

package main

import "fmt"

func main() {
    ch := make(chan int)

    go func() {
        for i := 1; i <= 5; i++ {
            ch <- i
        }
        close(ch)
    }()

    for v := range ch {
        fmt.Println(v)
    }
}

goroutine が 1 から 5 まで送信した後、channel を close しています。for range は close を検知すると自動的にループを終了します。

close を忘れると、range は永遠に待ち続けてデッドロックになるので注意してください。

close するのは送信側

一般的なルールとして、channel を close するのは送信側の責任です。

送信側

すべてのデータを送り終えたら close する。

受信側

close はしない。range や ok チェックで終了を検知する。

受信側が close すると、まだ送信しようとしている goroutine が panic を起こす可能性があります。

いつ close が必要か

close が必要なのは、受信側が「もう終わり」を知る必要があるときです。たとえば range ループで受信する場合や、複数の goroutine が終了を待っている場合などです。

逆に、受信回数が決まっていて、それだけ受信すれば済むなら close は不要なこともあります。