
Go言語switch文に似たselect文があるけど、どんな時に使うの?
Go言語ではswitch文に似た select
文があり、select文は複数のchannelを扱う時に便利な制御構文です。
今回は、select
文の基本を理解するために簡単なプログラムを例にお伝えしていきます。
Go言語のselectとは?
Go言語のselect文は複数のチャネルで受信を待ってもらうことができる機能を持った制御構文です。
channelは通常、値が入っていなければ受信をブロックしますが、select
文はブロックしないで処理する時に利用します。
まずはselect文を使ってみる
それでは、Go言語のselect文について簡単なプログラムを確認していきます。
channelのブロック状態を確認する
channelに値が入っていない状態で、channelを受信するプログラムを例にブロック状態を確認していきます。
// channelのブロック状態を確認
package main
import "fmt"
func main() {
// channelを2つ作成
ch1 := make(chan int, 2)
ch2 := make(chan string, 2)
// ch2のみデータを送信
ch2 <- "Golang"
// ch1にデータが無い状態で受信
a1 := <-ch1
a2 := <-ch2
// 値を出力
fmt.Println(a1)
fmt.Println(a2)
}
// 出力結果
$ go run main.go
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
/home/morip/go/src/golangstart/channel/channel_select/main.go:19 +0xab
exit status 2
deadlockエラーになりプログラムも強制終了します。
channelのブロック状態を解消する簡単なプログラム例
先程のプログラムでブロック状態を解消する、簡単なプログラム例を紹介します。

ここで select
文を使っていきます。
// channelのブロック状態を解消するプログラム
package main
import "fmt"
func main() {
// channelを2つ作成
ch1 := make(chan int, 2)
ch2 := make(chan string, 2)
// ch2のみデータを送信
ch2 <- "Golang"
select {
case a1 := <-ch1:
fmt.Println("ch1からのデータを受信しました。")
fmt.Println(a1)
case a2 := <-ch2:
fmt.Println("ch2からのデータを受信しました。")
fmt.Println(a2)
default:
fmt.Println("データはありません。")
}
}
// 出力結果
$ go run main.go
ch2からのデータを受信しました。
Golang
ブロック状態が解消され、プログラムが正常終了いたしました。
記述方法も switch文 と一緒ですが、select文
には switch文
と大きな違いがあります。

どういうことかと申しますと…下記のプログラムをご覧ください。
ch1
にもデータを送信してみました。
// select文のランダム評価を確認
package main
import "fmt"
func main() {
// channelを2つ作成
ch1 := make(chan int, 2)
ch2 := make(chan string, 2)
// 両方のchannelにデータを送信
ch1 <- 123
ch2 <- "Golang"
select {
case a1 := <-ch1:
fmt.Println("ch1からのデータを受信しました。")
fmt.Println(a1)
case a2 := <-ch2:
fmt.Println("ch2からのデータを受信しました。")
fmt.Println(a2)
default:
fmt.Println("データはありません。")
}
}
// 出力結果
$ go run main.go
ch2からのデータを受信しました。
Golang
または…
// 出力結果
$ go run main.go
ch1からのデータを受信しました。
123
プログラムを実行すると、出力結果の違いからわかるようにselect文はどれかひとつをランダムに評価していることが確認できます。
goroutineとchannelを使用したselect文
gorotineとchannelを使用したselect文のプログラムを確認していきます。

// goroutineでのselect文活用例
package main
import (
"fmt"
"time"
)
func tsetSelect1(ch chan string) {
// 1秒待ってch3にデータ送信
for {
ch <- "ch3のデータです。1秒待ってデータ送信しています。"
time.Sleep(1 * time.Second)
}
}
func testSelect2(ch chan string) {
// 2秒待ってch4にデータ送信
for {
ch <- "ch4のデータです。2秒待ってデータ送信しています。"
time.Sleep(2 * time.Second)
}
}
func main() {
ch3 := make(chan string)
ch4 := make(chan string)
go tsetSelect1(ch3)
go testSelect2(ch4)
// 送信されるデータを受信して評価
for i := 0; i < 6; i++ {
select {
case msg1 := <-ch3:
fmt.Println(msg1)
case msg2 := <-ch4:
fmt.Println(msg2)
}
}
}
// 出力結果
$ go run main.go
ch4のデータです。2秒おきにデータ送信しています。
ch3のデータです。1秒おきにデータ送信しています。
ch3のデータです。1秒おきにデータ送信しています。
ch3のデータです。1秒おきにデータ送信しています。
ch4のデータです。2秒おきにデータ送信しています。
ch3のデータです。1秒おきにデータ送信しています。
goroutineとchannelをそれぞれ2つ用意して、ch3
は1秒待ってデータを送信し ch4
は2秒待ってデータを送信しています。
main()
側では受信データを6回出力するプログラムです。

select
文でランダムに評価されているのがわかります。
【まとめ】select文でchannelのブロック状態を回避する
今回はselect文でchannelのブロックを回避して、プログラムを正常終了する方法についてお伝えしてきました。
以上です。
switch文とは記述方法が一緒ですが役割が違うことを理解しておきましょう。
goroutine・channel・selectの組み合わせは、deadlockエラーを回避する有効な手段ですので押さえておきましょう。
comment