いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-175 (N個のチャネルのどれかから値を取得)(reflect.Select, SelectCase)

概要

頻繁に使うことはありませんが、知っておくといざというときに便利かもしれません。

N個のチャネルを持っていて、その中のどれでもいいので値を受信したいってときがたまにあります。

そのような場合、reflectパッケージを使って処理すると動的な数のチャネルに対応できるときがあります。

サンプルを見たほうが早いと思いますので、以下に記載

サンプル

package reflectop

import (
    "reflect"

    "github.com/devlights/gomy/output"
)

// SelectCase -- reflect.SelectCase のサンプルです。
//
// REFERENCES:
//   - https://dev.to/hgsgtk/handling-with-arbitrary-channels-by-reflectselect-4d5g
func SelectCase() error {
    // 複数のチャネルを持っていて、その中のどれかのチャネルからでも良いので
    // データを取得していきたい場合、通常は for-select を使って処理することになる。
    // ただし、その場合、必要な分だけ case を増やしていかないと行けない.
    //
    // reflect.Select() を利用することで、リフレクションを利用して処理することが出来る

    const (
        chCount = 5
    )

    // チャネルを5つ用意
    chs := make([]chan int, chCount)
    for i := 0; i < chCount; i++ {
        ch := make(chan int)

        go func(i int) {
            ch <- i * i
        }(i)

        chs[i] = ch
    }

    // reflect.SelectCase を用意
    scs := make([]reflect.SelectCase, len(chs))
    for i, ch := range chs {
        sc := reflect.SelectCase{
            Dir:  reflect.SelectRecv,
            Chan: reflect.ValueOf(ch),
        }

        scs[i] = sc
    }

    // リフレクションを使って、複数のチャネルから一つ値を取得
    for i := 0; i < chCount; i++ {
        chosen, recv, ok := reflect.Select(scs)
        if ok {
            output.Stdoutf("reflect.Select", "chosen: %v\trecv: %v\n", chosen, recv)
        }
    }

    // 使ったチャネルを閉じる(このプログラムでは必要ないけど、お作法として)
    for _, ch := range chs {
        close(ch)
    }

    return nil
}

試してみる

実行すると以下のようになります。

gitpod /workspace/try-golang $ make run
go run github.com/devlights/try-golang/cmd/trygolang -onetime -example ""

ENTER EXAMPLE NAME: reflect_selectcase

[Name] "reflect_selectcase"
reflect.Select       chosen: 4  recv: 16
reflect.Select       chosen: 3  recv: 9
reflect.Select       chosen: 0  recv: 0
reflect.Select       chosen: 1  recv: 1
reflect.Select       chosen: 2  recv: 4


[Elapsed] 230.465µs

reflect.Select するたびにどれか一つのチャネルから値が取得出来ていますね。

参考情報

dev.to


過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com

github.com