Caution
お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。
select
『select』文はGoの並行処理で複数のchannelを同時に待ち受けるための構文です。switch文に似た見た目ですが、各caseがchannelの送受信操作を表します。準備できたchannelがランダムに選択されます。
構文
// selectで複数のchannelを同時に待ち受けます。
select {
case v := <-ch1:
// ch1からデータを受信したときの処理
case ch2 <- value:
// ch2にデータを送信できたときの処理
case v, ok := <-ch3:
// ch3からの受信とチャネルクローズ判定
if !ok {
// ch3がクローズされました。
}
default:
// どのchannelも準備できていない場合の処理(ノンブロッキング)
}
selectの特徴
| 特徴 | 概要 |
|---|---|
| ランダム選択 | 複数のcaseが同時に準備できている場合、Goがランダムにどれか1つを選択します。 |
| ブロッキング | defaultがない場合、いずれかのcaseが準備できるまでブロックします。 |
| ノンブロッキング | defaultを書くと、準備中のcaseがなければすぐにdefaultを実行します。 |
| タイムアウト実装 | time.Afterチャネルと組み合わせてタイムアウト処理を実装できます。 |
サンプルコード
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
// 別goroutineでchannelにデータを送ります。
go func() {
time.Sleep(100 * time.Millisecond)
ch1 <- "ch1からのメッセージ"
}()
go func() {
time.Sleep(200 * time.Millisecond)
ch2 <- "ch2からのメッセージ"
}()
// 2つのchannelからの受信をselectで待ちます。
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println("受信:", msg1)
case msg2 := <-ch2:
fmt.Println("受信:", msg2)
}
}
fmt.Println()
// タイムアウトの実装例です。
ch := make(chan int)
go func() {
time.Sleep(500 * time.Millisecond)
ch <- 42
}()
select {
case v := <-ch:
fmt.Println("受信:", v)
case <-time.After(300 * time.Millisecond):
fmt.Println("タイムアウトしました")
}
// ノンブロッキング受信の例です。
ch3 := make(chan int, 1)
select {
case v := <-ch3:
fmt.Println("受信:", v)
default:
fmt.Println("ch3にデータがありません(ノンブロッキング)")
}
}
概要
『select』はGoの並行処理の核心的な構文の一つです。複数のchannelを効率的に多重化したり、タイムアウト処理を簡潔に書いたりできます。goroutineに停止シグナルを送る際もselectとdone channelのパターンが標準的です。
selectにdefaultがない場合、どのcaseも準備できていないとselectが永久にブロックし、デッドロックが発生する可能性があります。タイムアウトかdefaultを使って必ず脱出できるように設計してください。
channelの基本については『channel』、goroutineの完了待ちは『sync.WaitGroup』を参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。