【问题标题】:Reading from a stream of go channels从 go 通道流中读取
【发布时间】:2019-08-04 14:03:04
【问题描述】:

我试图理解以下从通道通道读取的代码。我很难理解这个想法。

    bridge := func(done <-chan interface{}, chanStream <-chan <-chan interface{}) <-chan interface{} {
        outStream := make(chan interface{})
        go func() {
            defer close(outStream)
            for {
                var stream <-chan interface{}
                select {
                case <-done:
                    return
                case maybeSteram, ok := <-chanStream:
                    if ok == false {
                        return
                    }
                    stream = maybeSteram
                }
                for c := range orDone(done, stream) {
                    select {
                    case outStream <- c:
                    case <-done:  // Why we are selection from the done channel here?
                    }
                }
            }
        }()
        return outStream
    }

orDone 函数:

    orDone := func(done <-chan interface{}, inStream <-chan interface{}) <-chan interface{} {
        outStream := make(chan interface{})
        go func() {
            defer close(outStream)
            for {
                select {
                case <-done:
                    return
                case v, ok := <-inStream:
                    if ok == false {
                        return
                    }
                    select {
                    case outStream <- v:
                    case <-done:  // Again why we are only reading from this channel? Shouldn't we return from here?
                        // Why we are not retuening from here?
                    }
                }
            }
        }()
        return outStream
    }

正如评论中提到的,我需要一些帮助来理解我们为什么选择for c := range orDone(donem, stream)。谁能解释这里发生了什么?

提前致谢。

编辑

我从concurrency in go 中获取了代码。完整代码可以在这里找到:https://github.com/kat-co/concurrency-in-go-src/blob/master/concurrency-patterns-in-go/the-bridge-channel/fig-bridge-channel.go

【问题讨论】:

标签: go channel


【解决方案1】:

在这两种情况下,选择都是为了避免阻塞——如果读取器没有从我们的输出通道读取,写入可能会阻塞(甚至可能永远阻塞),但是我们希望 goroutine 在done 通道时终止已关闭,无需等待其他任何内容。通过使用select,它将等到任何一个事情发生,然后继续,而不是在检查done之前无限期地等待写入完成。

至于另一个问题,“我们为什么不回到这里?”:嗯,我们可以。但我们不必必须,因为一旦关闭,关闭的通道将永远保持可读(产生无限数量的零值)。所以在那些“底层”selects什么都不做也没关系;如果done 实际上是关闭的,我们将回到循环顶部并点击那里的case &lt;-done: return。我想这是风格问题。我可能自己写了return,但是这个示例的作者可能希望避免在两个地方处理相同的情况。只要它只是return 并不重要,但如果您想对done 执行一些附加 操作,如果底部选择,则必须在两个地方更新该行为返回,但只有在一个地方没有返回。

【讨论】:

    【解决方案2】:

    首先我将解释完成通道的用法。这是 Go 中许多与并发相关的包实现中遵循的常见模式。完成通道的目的是表示计算结束或更类似于停止信号。通常完成的通道将被许多 go-routines 或代码流中的多个位置监听。一个这样的例子是Go's builtin "context" package 中的完成频道。由于 Go 没有广播之类的功能,即向频道的所有听众发出信号(期望这个功能也不是一个好主意),人们只需关闭频道,所有听众都会收到 nil 值。在您的情况下,由于第二个 select 语句位于 for 块的末尾,因此代码所有者可能已决定继续循环,以便在下一次迭代时,侦听 done 通道的第一个 select 语句将从函数返回。

    【讨论】:

      猜你喜欢
      • 2016-01-24
      • 1970-01-01
      • 1970-01-01
      • 2020-05-26
      • 2022-11-17
      • 2015-11-08
      • 2023-02-06
      • 2021-10-07
      • 2014-09-30
      相关资源
      最近更新 更多