【问题标题】:Reading from multiple channels simultaneously in Golang在 Golang 中同时读取多个通道
【发布时间】:2014-01-02 19:02:30
【问题描述】:

我是 Golang 的新手。现在我正在尝试弄清楚如何在 Golang 中创建一个任意对一的频道,其中设置如下:

假设我有两个 goroutine numgen1 和 numgen2 同时执行并将数字分别写入通道 num1。编号2。我想在一个新进程 addnum 中添加从 numgen1 和 numgen2 发送的数字。我尝试过这样的事情:

func addnum(num1, num2, sum chan int) {
    done := make(chan bool)
    go func() {
        n1 := <- num1
        done <- true
    }()
        n2 := <- num2
        <- done
    sum <- n1 + n2
}

但这似乎很不正确。有人可以给我一些想法吗?

非常感谢您的帮助。

【问题讨论】:

    标签: concurrency go channels


    【解决方案1】:

    根据您的要求,您可能需要在每次迭代时阅读两个通道(即某种“zip”功能)。您可以通过选择来执行此操作,类似于user860302 的答案:

    func main() {
    
      c1 := make(chan int)
      c2 := make(chan int)
      out := make(chan int)
    
      go func(in1, in2 <-chan int, out chan<- int) {
        for {
          sum := 0
          select {
          case sum = <-in1:
            sum += <-in2
    
          case sum = <-in2:
            sum += <-in1
          }
          out <- sum
        }
      }(c1, c2, out)
    }
    

    这将永远运行。我首选的终止像这样的 goroutine 的方法是关闭输入通道。在这种情况下,您需要等待两者都关闭,然后在终止之前等待close(out)

    提示:注意使用定向通道作为 goroutine 形式参数。当您以这种方式编写时,编译器会捕获更多错误。幸福!

    【讨论】:

    • 请注意,您在内部函数中使用了 c1 和 c2 而不是 in1 和 in2。
    • 嗨,很好,特别是您的 in/out 注释,但请注意您在内部函数中使用了 c1 和 c2 而不是 in1 和 in2。此外,您将 sum 隐藏在选择中,因此当您将其发送出去时它为 0。抱歉重复发布。
    • 感谢您的更正 - 我已按照您的建议更新了代码。
    • 请将此建议与@publysher 的建议进行比较,这是实现类似目标的更简单方法。他的建议在其他 goroutine 的潜在更长阻塞无关紧要并且不会增加死锁风险的情况下会很好。如果有疑问,我上面列出的策略是更安全的选择。
    【解决方案2】:

    回答“同时从多个渠道读取”的问题

    有一种方法可以同时收听多个频道:

    func main() {
    
        c1 := make(chan string)
        c2 := make(chan string)
    
        ...
        go func() {
            for {
                select {
                    case msg1 := <- c1:
                    fmt.Println(msg1)
    
                    case msg2 := <- c2:
                    fmt.Println(msg2)
                 }
            }
        }()
    

    在本例中,我创建了一个通道 msg1 和 msg2。 然后我创建一个带有无限循环的 go 例程。在这个循环中,我听 msg1 AND msg2。 该系统允许您同时读取多个通道并在到达时处理消息。

    为了避免泄漏,我可能应该添加另一个通道来停止 goroutine。

    【讨论】:

    • 这不是同时读取,这是同时检查并在频道准备好时读取。所以,在现实世界中,您将需要同步代码。
    【解决方案3】:

    最简单的答案是

    func addnum(num1, num2, sum chan int) {
      n1 := <- num1
      n2 := <- num2
      sum <- n1 + n2
    }
    

    由于您需要 num1num2 来进行计算,否则这样做是没有意义的。毕竟有两种可能的执行顺序:

    1. num1 生成一个数字,后跟num2
    2. num2 生成一个数字,后跟num1

    在第一种情况下,我们的通道读取完全对应于执行顺序。在第二种情况下,我们的第一次读取将阻塞,直到num1 最终产生一个数字;由于num2 频道已经有一个数字,第二次读取将几乎立即完成。

    如果您想了解更多关于 Go 中的通道的信息,我建议您查看 http://godoc.org/github.com/thomas11/csp——这是 Hoare 用 Go 编写的 CSP 示例的集合。

    【讨论】:

    • 谢谢 :) 如果我想让频道连续读取,我可以分别为范围 num1 和范围 num2 写两个 for 循环吗?
    • 不,因为这将首先完全消耗range num1(可能永远不会),然后它才会从range num2 读取第一个条目。您可以将整个函数体包裹在 for { ... } 循环中以继续计算。
    • 这似乎比公认的答案更容易,而且“足够正确”。
    猜你喜欢
    • 1970-01-01
    • 2014-09-30
    • 2022-12-10
    • 2017-07-22
    • 2018-08-04
    • 1970-01-01
    • 1970-01-01
    • 2018-10-04
    • 2016-01-24
    相关资源
    最近更新 更多