いろいろ備忘録日記

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

Goメモ-75 (一つの入力を複製して2つにするチャネル, Tee)

概要

チャネル関数の続き。

あんまり使うことは無さそうですが、せっかく書いたのでメモメモ。

unix系のteeコマンドのように、一つの入力を2つに複製してくれるチャネルを生成する関数。

データを処理しながら、ついでに非同期で履歴出力とかする際に使えるかもしれませんね。

サンプル

package chans

// Tee -- Unix の tee コマンドのように一つの入力を2つに複製するチャネルを返します。
//
// noinspection GoNilness
func Tee(done <-chan struct{}, in <-chan interface{}) (<-chan interface{}, <-chan interface{}) {
    out1 := make(chan interface{})
    out2 := make(chan interface{})

    go func() {
        defer close(out1)
        defer close(out2)

        for v := range OrDone(done, in) {
            var ch1, ch2 = out1, out2
            for i := 0; i < 2; i++ {
                select {
                case ch1 <- v:
                    ch1 = nil
                case ch2 <- v:
                    ch2 = nil
                }
            }
        }
    }()

    return out1, out2
}

gomy/tee.go at master · devlights/gomy · GitHub

以下テストコードです。

package chans

import (
    "testing"
)

func TestTee(t *testing.T) {
    type (
        testin struct {
            data []interface{}
        }
        testout struct {
            result1 []interface{}
            result2 []interface{}
        }
        testcase struct {
            in  testin
            out testout
        }
    )

    cases := []testcase{
        {
            in: testin{
                data: []interface{}{
                    "hello",
                    "world",
                },
            },
            out: testout{
                result1: []interface{}{
                    "hello",
                    "world",
                },
                result2: []interface{}{
                    "hello",
                    "world",
                },
            },
        },
    }

    for _, c := range cases {
        func() {
            done := make(chan struct{})
            defer close(done)

            inCh := make(chan interface{}, len(c.in.data))
            func() {
                defer close(inCh)
                for _, v := range c.in.data {
                    inCh <- v
                }
            }()

            out1, out2 := Tee(done, inCh)

            result1 := make([]interface{}, 0, 0)
            result2 := make([]interface{}, 0, 0)
            for v := range out1 {
                v2 := <-out2

                t.Logf("out1: %v\tout2: %v", v, v2)

                result1 = append(result1, v)
                result2 = append(result2, v2)
            }

            if len(c.in.data) != len(result1) {
                t.Errorf("[len(c.in.data) != len(result1)] want: %d\tgot: %d", len(c.in.data), len(result1))
            }

            if len(c.in.data) != len(result2) {
                t.Errorf("[len(c.in.data) != len(result2)] want: %d\tgot: %d", len(c.in.data), len(result2))
            }

            if len(result1) != len(result2) {
                t.Errorf("[len(result1) != len(result2)] want: %d\tgot: (%d, %d)", len(c.in.data), len(result1), len(result2))
            }
        }()
    }
}

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

$ go test -v github.com/devlights/gomy/chans -run ^TestTee.*$
=== RUN   TestTee
    TestTee: tee_test.go:63: out1: hello        out2: hello
    TestTee: tee_test.go:63: out1: world        out2: world
--- PASS: TestTee (0.00s)
PASS
ok      github.com/devlights/gomy/chans 0.004s

元のチャネルから取得したデータを2つのチャネルに流しているので、2つのチャネルでちゃんと取得しないとブロックされてしまうのがネックですが、、、。

参考

Go言語による並行処理

Go言語による並行処理

関連記事

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com

devlights.hatenablog.com


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

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

devlights.github.io

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

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

github.com

github.com

github.com