select 文
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 62/107。
select 文を使用すると、ゴルーチンは複数のチャネル操作を同時に待機できます。これは 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 ケースを使用すると、select は非ブロック(non-blocking)になります。準備ができているチャネルがない場合、即座に実行されます。
select {
case msg := <-messages:
fmt.Println("Received:", msg)
default:
fmt.Println("No message available")
}一般的なパターンは、select と time.After を使用したタイムアウトを組み合わせる方法です。time.After は、指定された時間の経過後に値を受信するチャネルを返します:
select {
case result := <-ch:
fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
fmt.Println("Timeout!")
}これにより、チャネル操作が完了しない可能性がある場合に、プログラムが永遠に待ち続けるのを防ぐことができます。
チャレンジ
簡単複数の並行データソースを処理するために select ステートメントを使用するサービスモニターを構築しましょう。このモニターは、異なるサービスチャネルを同時にリッスンし、データが到着したとき、または更新が利用できないときに適切に応答します。
コードは2つのファイルに分けて構成します:
monitor.go: selectベースのチャネル処理を使用して、サービス監視ロジックを定義します。Name(string) とStatus(string) フィールドを持つServiceUpdate構造体を作成します。3つのチャネル
primary chan ServiceUpdate、backup chan ServiceUpdate、およびdone 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 動作の両方を実演します。実行するシナリオ(
primary、backup、stop、またはcheck)を決定するモードを入力から読み取ります。primary、backup、およびstopモードの場合:サービス名とステータスも読み取ります。3つのチャネルを作成し、モードに基づいて適切なチャネルにデータを送信するゴルーチンを起動し、その後Monitorを呼び出してその結果を出力します。checkモードの場合:サービス名とステータス、およびチェック前にデータを送信するかどうかを示すフラグ(sendまたはnosend)を読み取ります。容量1のバッファ付きチャネルを作成します。フラグがsendの場合、CheckStatusを呼び出す前にチャネルに更新を送信します。CheckStatusの結果を出力します。
以下の入力が提供されます:
- 1行目:モード(
primary、backup、stop、またはcheck) - 2行目:サービス名
- 3行目:サービスステータス
- 4行目(
checkモードのみ):sendまたはnosend
例えば、以下が与えられた場合:
primary
Database
healthy出力は以下のようになります:
Primary: Database is healthyまた、以下が与えられた場合:
check
Cache
active
nosend出力は以下のようになります:
No updates pendingチートシート
select 文を使用すると、ゴルーチンは複数のチャネル操作を同時に待機できます。これは switch 文のように機能しますが、各 case はチャネルの送信または受信を伴います:
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
}複数のチャネルの準備ができている場合、select はランダムに1つを選択します。select がないと、1つのチャネルからの受信がブロックされ、別のチャネルのデータを逃す可能性があります。
default case を使用すると、select はノンブロッキングになります。どのチャネルの準備もできていない場合、即座に実行されます:
select {
case msg := <-messages:
fmt.Println("Received:", msg)
default:
fmt.Println("No message available")
}一般的なパターンとして、select と time.After を使用したタイムアウトの組み合わせがあります。time.After は、指定された時間の経過後に値を受信するチャネルを返します:
select {
case result := <-ch:
fmt.Println("Got result:", result)
case <-time.After(2 * time.Second):
fmt.Println("Timeout!")
}これにより、チャネル操作が完了しない可能性がある場合に、プログラムが永遠に待機し続けるのを防ぐことができます。
自分で試してみよう
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)を作成する
// - 適切なチャネルにデータを送信するゴルーチンを起動する
// - Monitor を呼び出し、結果を出力する
// モードが "check" の場合:
// - 追加のフラグ("send" または "nosend")を読み取る
// - キャパシティ1のバッファ付きチャネルを作成する
// - フラグが "send" の場合、チャネルに更新を送信する
// - CheckStatus を呼び出し、結果を出力する
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー