有时候你可能想分割来自通道的多个值,以便将它们发送到两个独立区域。想象一下:你可能想要在一个通道上接收一系列操作指令,将它们发送给执行者,同时记录操作日志。

与Unix系统的tee命令功能类似,我们用tee-channel来实现同样的功能。你可以传递给它一个用作读取的通道,它会返回两个单独的通道:

tee := func(
    done <-chan interface{},
    in <-chan interface{},
) (_, _ <-chan interface{}) { <-chan interface{}) {

    out1 := make(chan interface{})
    out2 := make(chan interface{})

    go func() {

        defer close(out1)
        defer close(out2)

        for val := range orDone(done, in) {
            var out1, out2 = out1, out2 //1
            for i := 0; i < 2; i++ { //2
                select {
                case <-done:
                case out1 <- val:
                    out1 = nil //3
                case out2 <- val:
                    out2 = nil //3
                }
            }
        }
    }()

    return out1, out2
}

注意:原文例子就是这样,反复确认没有贴错。大家就当伪码看吧

  1. 我们希望使用使用本地的变量,所以建立了他们的副本。
  2. 我们将使用一条select语句,以便写入out1和out2不会彼此阻塞。 为了确保两者都顺利写入,我们将执行select语句的两个迭代。
  3. 一旦我们写入了通道,我们将其副本设置为零,这样继续写入将阻塞,而另一个通道可以继续执行。

注意写入out1和out2是紧密耦合的。 直到out1和out2都被写入,迭代才能继续。 通常这不是问题,因为无论如何,处理来自每个通道的读取流程的吞吐量应该是tee之外的关注点,但值得注意。 这是一个快速调用示例:

done := make(chan interface{})
defer close(done)

out1, out2 := tee(done, take(done, repeat(done, 1, 2), 4))

for val1 := range out1 {
    fmt.Printf("out1: %v, out2: %v\n", val1, <-out2)
}

利用这种模式,很容易使用通道作为系统数据的连接点。

最后编辑: kuteng  文档更新时间: 2021-01-02 17:30   作者:kuteng