キモい select を理解する 3 パターン
お久しぶりです。テスト期間中です。今日は Go 言語の select ステートメントの挙動が頭おかしいので、理解するために 3 パターンに挙動を分けてみようという話です。
Q. お前本当にわかってんの?
A. 俺を信じるな、仕様を信じろ
http://golang.org/ref/spec#Select_statements
まず select ステートメントとは
select { case value := <- channel: // 受信成功時 case channel <- value: // 送信成功時 default: // 何もできなかった時 }
の書き方で、チャネル操作をしたあと、任意の動作をするてきなやつです。
チャネル操作ができるか、というのは
case <- channel:
のとき- 他のゴルーチンで
channel <- hoge
を待機中 case channel <- hoge:
のとき- 他のゴルーチンで
<- channel
を待機中
かどうかです。
1. 操作可能なチャネルが 1 つあるとき
これが一番直感的な挙動で、そのチャネル操作を実行します。
a := make(chan int) b := make(chan int) go func() { a <- 0 }() select { case <-a: fmt.Println("a") case <-b: fmt.Println("b") } // a が出力
2. 操作可能なチャネルがないとき
この場合では、 default:
があるかどうかで変わってきます。
default
がある場合
default
ブロックが実行されます。
c := make(chan int) select { case <-c: fmt.Println("receive") default: fmt.Println("default") } // default が出力
default
がない場合
どれかが実行可能になるまで待機します。
select { case <-time.After(1 * time.Second): fmt.Println("1 sec") case <-time.After(2 * time.Second): fmt.Println("2 sec") } // 1 sec が出力
3. 複数のチャネルが操作可能なとき
この場合では、実行される case
はランダムで決まります。
a := make(chan int) b := make(chan int) c := make(chan int) go func() { for { a <- 0 } }() go func() { for { b <- 0 } }() go func() { for { <-c } }() for i := 0; i < 10; i++ { select { case <-a: fmt.Println("a") case <-b: fmt.Println("b") case c <- 0: fmt.Println("c") } } /* a c a b c c a b c a */