序文
Go 言語では、select はチャネルに関連する IO 操作を監視するために使用されるキーワードです。
select ステートメントを使用すると、複数のチャネルを同時に監視し、いずれかのチャネルの準備ができたときに対応する処理を実行できます。
この記事ではselect文の一般的な使い方と使用時の注意点をまとめます。
基本的な文法
select ステートメントの基本的な構文は次のとおりです。
select {
case <-channel1:
// 通道 channel1 就绪时的处理逻辑
case data := <-channel2:
// 通道 channel2 就绪时的处理逻辑
default:
// 当没有任何通道就绪时的默认处理逻辑
}
この構文を見ると、switch ステートメントを簡単に考えることができます。
select ステートメントと switch ステートメントは表面的には似ていますが、目的と機能が異なります。
スイッチは条件判定に使用し、セレクトはチャンネル操作に使用します。select ステートメントではいかなる種類の条件式も使用できません。チャネルに対してのみ操作できます。
利用ルール
構文はシンプルですが、使用する際に注意が必要な点がいくつかありますので、以下の4点にまとめました。
select ステートメントは、チャネル操作、複数のチャネル間の選択、チャネルの準備完了状態の監視にのみ使用でき、他のタイプの条件判断には使用できません。
select ステートメントには複数の case 句を含めることができ、各 case 句はチャネル操作に対応します。いずれかのチャネルの準備が完了すると、対応する case 節が実行されます。
複数のチャネルが準備できている場合、select ステートメントは実行するチャネルをランダムに選択します。これにより、複数のチャネル間での公平な競争が保証されます。
select ステートメントの実行は、ブロッキングまたはノンブロッキングの場合があります。どのチャネルも準備ができておらず、default 句がない場合、select ステートメントはチャネルの準備ができるまでブロックされます。デフォルト句があり、準備ができているチャネルがない場合、select ステートメントはデフォルト句を実行し、ブロックを回避します。
多重化
select の最も一般的な使用法の 1 つは、複数のチャネルを同時にリッスンし、その準備状況に基づいてさまざまなアクションを実行することです。
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
time.Sleep(3 * time.Second)
c1 <- "one"
}()
go func() {
time.Sleep(3 * time.Second)
c2 <- "two"
}()
select {
case msg := <-c1:
fmt.Println(msg)
case msg := <-c2:
fmt.Println(msg)
}
}
上記のコードを実行すると、プログラムは 1 つまたは 2 つをランダムに出力します。チャンネルが空の場合、プログラムは常にそこでブロックされます。
ノンブロッキング通信
チャネル内に読み取るデータがない場合、または書き込むバッファ領域がない場合、通常の読み取りおよび書き込み操作はブロックされます。
しかし、select ステートメントを使用すると、データの準備ができていないときにデフォルトのロジックを実行できるため、プログラムが無限待機状態に陥るのを避けることができます。
package main
import (
"fmt"
)
func main() {
channel := make(chan int)
select {
case data := <-channel:
fmt.Println("Received:", data)
default:
fmt.Println("No data available.")
}
}
上記のコードを実行すると、プログラムはデフォルトのブランチを実行します。
出力:
No data available.
タイムアウト処理
select 関数と time.After 関数を組み合わせることで、指定した時間内にチャネルの準備が完了するのを待ち、時間が経過した後に対応するロジックを実行できます。
package main
import (
"fmt"
"time"
)
func main() {
channel := make(chan int)
select {
case data := <-channel:
fmt.Println("Received:", data)
case <-time.After(3 * time.Second):
fmt.Println("Timeout occurred.")
}
}
上記のコードを実行し、チャネルに 3 秒以内に読み込むデータがない場合は、分岐後の時間を選択して実行します。
出力:
Timeout occurred.
以上がこの記事の全内容となりますが、悪くないと思っていただけましたら、いいね、リポスト、フォローをよろしくお願いいたします。