Menu
Coddy logo textTech

select 文

CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 62/107。

select ステートメントを使用すると、goroutine は複数のチャネル操作を同時に待機できます。これは switch ステートメントに似ていますが、各 case にはチャネルの送信または受信が含まれます。複数のチャネルが準備できている場合、select はそのうち 1 つをランダムに選択します。

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    go func() { ch1 <- "from channel 1" }()
    go func() { ch2 <- "from channel 2" }()
    
    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    }
}

selectなしでは、ch1からの受信はデータが届くまでブロックされ、ch2のデータを取り逃す可能性があります。selectを使うと、先にデータを受信したチャンネルが処理されます。

default case によって、select は非ブロッキングになります。準備完了しているチャネルがない場合、すぐに実行されます。

select {
case msg := <-messages:
    fmt.Println("Received:", msg)
default:
    fmt.Println("No message available")
}

一般的なパターンでは、selecttime.After をタイムアウトと組み合わせます。time.After は、指定した時間が経過した後に値を受信するチャネルを返します。

select {
case result := <-ch:
    fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
    fmt.Println("Timeout!")
}

これにより、チャネル操作が完了しない可能性がある場合に、プログラムが永遠に待機し続けるのを防げます。

challenge icon

チャレンジ

簡単

select文を使用して複数の同時実行データソースを処理するサービスモニターを作成しましょう。モニターは異なるサービスチャネルを同時に監視し、データが到着したとき、または利用可能な更新がないときに適切に応答します。

コードを2つのファイルに分けて整理します。

  • monitor.goselectベースのチャネル処理を使用して、サービス監視ロジックを定義します。

    Name(string)フィールドとStatus(string)フィールドを持つServiceUpdate構造体を作成します。

    3つのチャネル、primary chan ServiceUpdatebackup chan ServiceUpdatedone chan boolを受け取るMonitor関数を実装します。この関数ではselectを使用して、3つすべてのチャネルを同時に監視します。

    • primaryから受信した場合:整形した文字列Primary: [Name] is [Status]を返します
    • backupから受信した場合:整形した文字列Backup: [Name] is [Status]を返します
    • doneから受信した場合:Monitoring stoppedを返します

    単一のupdates chan ServiceUpdateを受け取り、defaultケースを持つselectを使用してノンブロッキングチェックを実行するCheckStatus関数を実装します。

    • 更新が利用可能な場合:Update available: [Name]を返します
    • 更新が利用可能でない場合(defaultケース):No updates pendingを返します
  • main.go:チャネルを設定し、ブロッキングおよびノンブロッキングのselect動作を示します。

    実行するシナリオを決定するモードを入力から読み取ります:primarybackupstop、またはcheck

    primarybackupstopモードの場合:サービス名とステータスも読み取ります。3つのチャネルを作成し、モードに基づいて適切なチャネルにデータを送信するgoroutineを起動してから、Monitorを呼び出し、その結果を出力します。

    checkモードの場合:サービス名とステータスに加え、チェック前にデータを送信するかどうかを示すフラグ(sendまたはnosend)を読み取ります。capacity 1のbufferedチャネルを作成します。フラグがsendの場合は、CheckStatusを呼び出す前にチャネルへ更新を送信します。CheckStatusの結果を出力します。

次の入力が提供されます。

  • 1行目:モード(primarybackupstop、またはcheck
  • 2行目:サービス名
  • 3行目:サービスステータス
  • 4行目(checkモードの場合のみ):sendまたはnosend

たとえば、次の入力が与えられた場合:

primary
Database
healthy

出力は次のようになります。

Primary: Database is healthy

また、次の入力が与えられた場合:

check
Cache
active
nosend

出力は次のようになります。

No updates pending

自分で試してみよう

package main

import (
	"fmt"
)

func main() {
	var mode string
	fmt.Scanln(&mode)

	var serviceName string
	fmt.Scanln(&serviceName)

	var serviceStatus string
	fmt.Scanln(&serviceStatus)

	// TODO: 異なるモードを処理する: "primary", "backup", "stop", "check"
	
	// モード "primary", "backup", "stop" の場合:
	// - 3つのチャネルを作成する: primary, backup, done
	// - 適切なチャネルにデータを送信する goroutine を起動する
	// - Monitor を呼び出して結果を出力する
	
	// モード "check" の場合:
	// - 追加のフラグを読み取る ("send" または "nosend")
	// - 容量 1 のバッファ付きチャネルを作成する
	// - フラグが "send" の場合、チャネルに更新を送信する
	// - CheckStatus を呼び出して結果を出力する
	
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Goオンラインコンパイラ