コンテンツにスキップ

Go/select

出典: フリー教科書『ウィキブックス(Wikibooks)』
< Go


Goにおける select キーワードは、複数のチャネル操作(送信または受信)のいずれかが準備完了するのを待ち受けるための制御構造です。select 文は、ゴルーチンが複数の通信チャネルからのイベントを効率的に処理するために不可欠な機能です。

select 文の基本的な構文は以下の通りです。

select {
case <-ch1:
    // ch1 から受信した場合の処理
case ch2 <- value:
    // ch2 へ送信できた場合の処理
case <-ch3:
    // ch3 から受信した場合の処理
default:
    // どのチャネルも準備完了していない場合の処理 (省略可能)
}

select 文の動作

[編集]
  1. select 文は、記述された複数の case のチャネル操作を同時に評価します。
  2. 準備完了した(送信可能または受信可能になった)チャネル操作に対応する case のブロック内のコードが実行されます。
  3. 複数の case が同時に準備完了した場合、Go はそのうちの一つをランダムに選択して実行します。
  4. どの case もすぐに準備完了しない場合、default ケースが存在すれば、そのブロック内のコードが実行されます。default ケースが省略されている場合、select 文はいずれかのチャネル操作が準備完了するまでブロックします。

select 文の主な用途

[編集]
  • 複数のチャネルからの受信: 複数のゴルーチンからの結果やイベントを待ち受ける場合に便利です。
  • ノンブロッキングなチャネル操作: default ケースを使用することで、チャネルがすぐに準備できていない場合にブロックせずに処理を続行できます。
  • タイムアウト処理: time.After 関数と組み合わせて、チャネル操作のタイムアウトを実装できます。
  • 終了シグナルの監視: 特定の終了チャネルからの信号を監視し、ゴルーチンの終了処理を行うことができます。

select 文の例

[編集]

1. 複数のチャネルからの受信

[編集]
package main

import (
	"fmt"
	"time"
)

func main() {
	ch1 := make(chan string)
	ch2 := make(chan string)

	go func() {
		time.Sleep(1 * time.Second)
		ch1 <- "メッセージ from ch1"
	}()

	go func() {
		time.Sleep(2 * time.Second)
		ch2 <- "メッセージ from ch2"
	}()

	for i := 0; i < 2; i++ {
		select {
		case msg1 := <-ch1:
			fmt.Println("受信:", msg1)
		case msg2 := <-ch2:
			fmt.Println("受信:", msg2)
		}
	}
}

この例では、select 文は ch1ch2 の両方からの受信を待ち受けます。最初にメッセージが送信されたチャネルの case が実行されます。

2. ノンブロッキングなチャネル操作

[編集]
package main

import "fmt"

func main() {
	ch := make(chan int)

	select {
	case val := <-ch:
		fmt.Println("受信:", val)
	default:
		fmt.Println("チャネルは準備できていません")
	}

	// 送信も同様にノンブロッキングに試みることができる
	select {
	case ch <- 10:
		fmt.Println("送信しました")
	default:
		fmt.Println("チャネルは送信準備ができていません")
	}
}

default ケースがあるため、チャネルがすぐに受信または送信の準備ができていない場合でも、select 文はブロックせずに default ケースの処理を実行します。

3. タイムアウト処理

[編集]
package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan string)

	select {
	case msg := <-ch:
		fmt.Println("受信:", msg)
	case <-time.After(3 * time.Second):
		fmt.Println("タイムアウトしました")
	}
}

time.After 関数は、指定された時間が経過後に値を送信するチャネルを返します。select 文はこのチャネルからの受信も監視することで、チャネル操作が指定時間内に完了しない場合にタイムアウト処理を行うことができます。

まとめ

[編集]

select キーワードは、Goにおける並行処理において、複数のチャネルとの効率的な通信を実現するための重要な構文です。複数のチャネルからの受信、ノンブロッキングな操作、タイムアウト処理、終了シグナルの監視など、様々な並行処理のパターンを簡潔に記述することができます。