第2の人生の構築ログ

自分の好きなことをやりつつ、インカムもしっかりと。FIRA60 (Financial Independence, Retire Around 60) の実現を目指します。SE を生業としていますが、自分でプログラミングしながら自分が欲しいと思うアプリケーションを作ることが楽しみです。旅行と温泉、音楽と読書は欠かすことができません。

【Go言語】チャネル (channel) のクローズ (close) 〜 チャネルのバッファ内の値を確実に全て取り出す

チャネルの状態には、オープンとクローズ(closed)の状態があり、make することで生成された チャネル はオープンな状態となり、close(チャネル) で明示的に閉じることができます。
チャネル が close されたことを複数のゴルーチン (goroutine) へ一斉に伝えることができますので、close 関数を使ってブロードキャストによるキャンセル処理を実装できます。

なお、この手の処理は今はコンテキスト処理を使って行う方がよいのだと思います。ただ、この素の状態でのやり方も見ておいて損はないでしょう。。

以下は状況把握のためのサンプルになります。

3つのバッファをもつ int 型の チャネル を作成し、値を3つ送信した後に直ぐにクローズします。

case i, more := <-ch: については補足します。
チャネル を受信する際に戻り値に2つの変数を用意すると、1つ目(i)にはその値、2つ目(more)にはチャネルのオープン状態の真偽値(true/false)が入ってきます。 以下はチャネルを受信する際のイディオム for ループ、select の構文となっていますが、select でチャネルを受信後、そのチャネルのオープン状態(more)をチェックすることで処理の終了(ループの終了)を判断しています。

package main

import "fmt"

func main() {
    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3
    close(ch)

    for {
        select {
        case i, more := <-ch:
            if more {
                fmt.Printf("OpenStatus: %v, value: %d\n", more, i)
            } else {
                fmt.Printf("OpenStatus: %v, value: %d\n", more, i)
                fmt.Println("exit for loop")
                goto L
            }
        }
    }
L:
    fmt.Println("Finish!")
}

上記の実行結果は以下の通りです。

OpenStatus: true, value: 1
OpenStatus: true, value: 2
OpenStatus: true, value: 3
OpenStatus: false, value: 0
exit for loop
Finish!

この結果からわかることとしまして、

  • チャネルはクローズされていても、また、バッファ内が空になっていても受信は行える。
  • チャネルのバッファが空で、かつ、クローズされた状態の場合に、チャネルのオープン状態を保持する変数(more)はfalseになる。

この結果からもチャネルのオープン状態を保持する変数(more)は確実にチャネルのバッファ内にある値をもれなく取り出せることを保証していますので、上記のような最後まで取り出して完了、という判断に利用できます。