読者です 読者をやめる 読者になる 読者になる

アジョブジ星通信

日常系バンザイ。

キモい 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
*/

簡単ですね!

#いいえ

こちらからは以上です