goroumaru41gou

遊んでいる中でインプットした内容をアウトプットする場

並行処理について(teeチャンネル編)

1つのチャンネル値を2つに別けて別の場所で使用したいとき、teeチャンネルを使うことについて、備忘録とする。

参考にしたのはこちら

ネットで調べるのもいいけど、情報の正しさを判断できないなら、この本に記載されてるコードを見た方が良いと思う。

取り扱うもの

テストコード

github.com/goroumaru/test-code

teeチャンネルとは?

ひとつのチャンネルデータを、ふたつのチャンネルへ分割する。

だから、1入力2出力を"T"という文字で表して、teeチャンネル。

S/Wだけでなく、H/W分野でもteeチャンネルって言葉は使われてる。

teeチャンネル

// Tee :
func Tee(ctx context.Context, in <-chan interface{}) (_, _ <-chan interface{}) {
    out1 := make(chan interface{})
    out2 := make(chan interface{})

    go func() {
        defer close(out1)
        defer close(out2)
        for val := range orDoneChannel.OrDoneCtx(ctx, in) {
            var out1, out2 = out1, out2 // コピーしてローカル変数用意
            for i := 0; i < 2; i++ {    // out1とout2を確実に選択するため。
                select {
                case out1 <- val:
                    out1 = nil
            // コピー側へnil代入し、out1チャンネルをブロックさせる。
            // (=out2を選択させる)
                case out2 <- val:
                    out2 = nil
            // コピー側へnil代入し、out2チャンネルをブロックさせる。
            // (=out1を選択させる)
                }
            }
            // out1とout2の書き込みが終わると、inチャンネルが読み込み可能となる。
            // for ~ rangeのイテレーションがひとつ進むから。
        }
    }()
    return out1, out2
}

なぜteeチャンネルを利用するのか?

チャンネルブロックを避けて、2チャンネルへ分岐できる。

teeチャンネル使い方

func TestTeeChannel(t *testing.T) {

    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    // 定時実行
    tick := time.NewTicker(1 * time.Second)
    defer tick.Stop()

    inData := make(chan interface{})
    go func() {
        defer close(inData)
        var cnt int
        for {
            select {
            case <-tick.C:
                cnt++
                inData <- cnt
            case <-ctx.Done():
                return
            }
        }
    }()

    // teeチャンネルを利用してチャンネルを分岐する
    out1, out2 := teeChannel.Tee(ctx, inData) // teeChannelというパッケージ名にしたので・・・

    for v1 := range out1 {
        v2 := <-out2
        fmt.Printf("2つに分割:\n(out1, ok) = (%v)\n(out2, ok) = (%v)\n", v1, v2)
    }
}

参考