https://free-engineer.life/golang-select/

select文はチャネルの送受信操作を多重化できる。

select文の構文

select文の書き方は、switch文に似ている。

select {
case <-ch1:
	// ch1から受信したときに実行される処理
case v := <-c2:
	// ch2から受信したときに実行される処理
	// 変数に値を入れることもできる
case ch3 <- y:
	// ch3に送信したときに実行される処理
default:
	// どのcaseも準備できていないときに実行される処理(省略可能)
}

ポイントは、「select文は上から順番に評価されない」こと。

チャネルへの送受信は実行可能かを判断して、可能であれば実行される。

つまり、送信の場合は「キャパシティ(バッファ)がいっぱいになっていないか」、受信の場合は、「チャネルに値が送信されたか、チャネルが閉じられたか」を確認し、処理を進める準備が出来ていれば実行される。

もし、どのチャネルも準備が出来ていなければ、defaultが実行される。もしdefaultを省略していたらselect文全体がブロックされる。

複数のcaseが実行可能な状況だった場合、どれか1つのcaseがランダムに実行される。

default節を省略したサンプルコード

defaultを省略した状態でselect文がブロックされるのを確認する。

package main

import (
	"fmt"
	"time"
)

func main() {
	start := time.Now()
	ch := make(chan struct{})
	go func() {
		time.Sleep(5 * time.Second)
		close(ch)
	}()

	fmt.Println("Selectブロック中")
	select {
	case <-ch:
		fmt.Printf("ch1 close. ブロック時間: %v\\n", time.Since(start))
	}
}

実行結果は

$ go run main.go
Selectブロック中
ch1 close. ブロック時間: 5.005249353s

default節を用意した場合のサンプルコード

すべてのcase文が準備が整っていない場合、default節が実行される。

package main

import "fmt"

func main() {
	ch1 := make(chan struct{})
	ch2 := make(chan struct{})
	// ch1, ch2ともに宣言しただけで、何も送信されてこない(受信することはない)
	// そのため、default節が実行される
	select {
	case <-ch1:
		fmt.Println("ch1")
	case <-ch2:
		fmt.Println("ch2")
	default:
		fmt.Println("default")
	}
}

実行結果は