メインコンテンツにスキップ
モジュール 7 · レッスン 3 / 5コース内の 33/50~12 min
モジュールのレッスン (3/5)

`select`: チャネルを多重化する

select は 複数のチャネル操作を同時に待機します。 これは Go 同時実行性の switch です。最初の処理に反応できるようになります。 多くのチャネルの準備ができています。

構文

Go
select {
case v := <-ch1:
    fmt.Println("ch1:", v)
case v := <-ch2:
    fmt.Println("ch2:", v)
case ch3 <- 99:
    fmt.Println("inviato su ch3")
case <-time.After(time.Second):
    fmt.Println("timeout")
default:
    fmt.Println("nessuno è pronto")
}

セマンティクス:

  • すべてのケースが評価されます。準備ができたケースのうち 1 つだけが選択されます。
  • 複数のケースが用意されている場合、選択肢は 擬似ランダム (順序付けなし) です。
  • 準備ができているケースがない場合: 準備ができるまで select がブロックされます。
  • default がある場合: ケースの準備ができていないとすぐに実行されます (非ブロッキング 選択)。

タイムアウトパターン

Go
select {
case res := <-fetch(url):
    use(res)
case <-time.After(2 * time.Second):
    log.Println("timeout: fetch troppo lento")
}

time.After(d) は、d の後の値を生成する <-chan Time を返します。 これは待ち時間を制限する古典的な方法です。

デフォルトでのノンブロッキング選択

Go
select {
case msg := <-ch:
    process(msg)
default:
    // niente da fare, prosegui
}

ブロックせずにチャネルを「ポーリング」します。コードパスで役立ちます。 強制的に待つことなく応答し続ける必要があります。

「完了チャンネル」パターン

select とキャンセル チャネルを組み合わせます。

Go
func worker(in <-chan Job, done <-chan struct{}) {
    for {
        select {
        case j := <-in:
            process(j)
        case <-done:
            return       // chi gestisce done segnala lo shutdown
        }
    }
}

コーディネーターからの close(done) は、すべてのイベントで case <-done をトリガーします 待機中のゴルーチン: 協調シャットダウン。

選択によるループ

Go
for {
    select {
    case v := <-input:
        if v == nil {
            return
        }
        handle(v)
    case <-time.After(5 * time.Second):
        keepAlive()
    case <-ctx.Done():
        return
    }
}

サーバーとワーカーの典型的な「イベント ループ」パターン。

「無効」ケースとしての nil チャネル

高度なテクニック: case <-ch と ch == nil は 決して 選択されました。これにより、ケースを動的に「無効化」できます。

Go
var in chan int = source()
for {
    select {
    case v, ok := <-in:
        if !ok {
            in = nil    // disabilita questo case
            continue
        }
        handle(v)
    case <-ctx.Done():
        return
    }
}

in が閉じられると、それを null にします。select は、 他の場合。

試してみてください

運動#go.m7.l3.e1
試行回数: 0読み込み中…

ch1 と ch2 から受信する 2 つのケースで選択を実装します。どちらが先に到着したかを印刷します。

エディターを読み込み中…
ヒントを表示

構文 `select { case ...: ... }` はチャネルごとに 1 つのケースを使用します。

3 回の試行後に解決策が利用可能になります

運動#go.m7.l3.e2
試行回数: 0読み込み中…

time.After(time.Second) を使用してタイムアウト ケースを選択に追加します。

エディターを読み込み中…
ヒントを表示

`time.After(d)` は d 以降を受信するチャネルです。

3 回の試行後に解決策が利用可能になります

Quiz#go.m7.l3.e3
準備完了

選択が非ブロックになるのはなぜですか?

Go
select { case v := <-ch: ... ??? }
回答の選択肢

要約

  • select は、いくつかのチャネル操作のうち最初の準備完了ケースを待ちます。
  • 複数の準備済みケースの中から擬似ランダムに選択します。
  • default → 非ブロッキング選択 (ポーリング)。
  • タイムアウト用の time.After(d) (ループ内のリークに注意してください。context を使用してください)。
  • パターン: 完了チャネル、イベントループ、ケースを無効にする nil チャネル。
  • 空の select{} = 永久ブロック: ほとんどの場合、バグです。