Concurrency 可以将多个通道多路复用为一个通道吗?

Concurrency 可以将多个通道多路复用为一个通道吗?,concurrency,go,channel,Concurrency,Go,Channel,其思想是在一个片中具有可变数量的通道,将通过它们接收的每个值推送到单个通道中,并在最后一个输入通道关闭后关闭该输出通道。类似于此,但对于多个频道,不止两个: func multiplex(cin1, cin2, cout chan int) { n := 2 for { select { case v, ok := <-cin1: if ok { cout <- v

其思想是在一个片中具有可变数量的通道,将通过它们接收的每个值推送到单个通道中,并在最后一个输入通道关闭后关闭该输出通道。类似于此,但对于多个频道,不止两个:

func multiplex(cin1, cin2, cout chan int) {
    n := 2
    for {
        select {
        case v, ok := <-cin1:
            if ok {
                cout <- v
            } else {
                n -= 1
            }

        case v, ok := <-cin2:
            if ok {
                cout <- v
            } else {
                n -= 1
            }
        }

        if n == 0 {
            close(cout)
            break
        }
    }
}
func多路复用(cin1、cin2、cout chan int){
n:=2
为了{
挑选{

案例五,好:=我用goroutines制作了这个。这是你想要的吗

package main

import (
    "fmt"
)

func multiplex(cin []chan int, cout chan int) {
    n := len(cin)
    for _, ch := range cin {
        go func(src chan int) {
            for {
                v, ok := <-src
                if ok {
                    cout <- v
                } else {
                    n-- // a little dangerous. Maybe use a channel to avoid missed decrements
                    if n == 0 {
                        close(cout)
                    }
                    break
                }
            }
        }(ch)
    }
}

// a main to test the multiplex
func main() {
    cin := make([]chan int, 3)
    cin[0] = make(chan int, 2)
    cin[1] = make(chan int, 2)
    cin[2] = make(chan int, 2)
    cout := make(chan int, 2)
    multiplex(cin, cout)
    cin[1] <- 1
    cin[0] <- 2
    cin[2] <- 3
    cin[1] <- 4
    cin[0] <- 5
    close(cin[1])
    close(cin[0])
    close(cin[2])
    for {
        v, ok := <-cout
        if ok {
            fmt.Println(v)
        } else {
            break
        }
    }
}
主程序包
进口(
“fmt”
)
func多路复用(cin[]信道内部,信道内部){
n:=len(cin)
对于u,ch:=范围cin{
go func(src chan int){
为了{

v、 确定:=编辑:添加两两减少示例代码,并重新排序部分答案

首选的解决方案是不回答“重组,这样你就没有一块通道了。”重组通常可以利用多个goroutine可以发送到单个通道的功能。因此,与其让每个源在单独的通道上发送,然后处理从一组通道接收,不如创建一个通道,让所有源在该通道上发送

Go不提供从一个频道片段接收的功能。这是一个常见问题,虽然刚才给出的解决方案是首选方案,但还是有办法对其进行编程。我认为您在最初的问题中建议的解决方案是“减少片段成对”这是一个二进制分治解决方案。只要您有一个将两个通道多路复用为一个通道的解决方案,它就可以正常工作。您的示例代码非常接近

您只是缺少了一个使示例代码正常工作的小技巧。在减少n的地方,添加一行以将通道变量设置为nil。例如,我让代码读取

    case v, ok := <-cin1:
        if ok {
            cout <- v
        } else {
            n--
            cin1 = nil
        }
    case v, ok := <-cin2:
        if ok {
            cout <- v
        } else {
            n--
            cin2 = nil
        }
    }

案例五,好:=我相信这段代码就是你想要的。我已经更改了签名,因此很明显,输入和输出应该只用于一个方向的通信。请注意,添加了一个,你需要某种方式让所有输入发出信号,表明它们已经完成,这非常简单

func combine(inputs []<-chan int, output chan<- int) {
  var group sync.WaitGroup
  for i := range inputs {
    group.Add(1)
    go func(input <-chan int) {
      for val := range input {
        output <- val
      }
      group.Done()
    } (inputs[i])
  }
  go func() {
    group.Wait()
    close(output)
  } ()
}

func combine(inputs[]文档说,如果您从带有“ok”的通道读取值,则操作不会阻塞。
ok
的值只是
false
,然后继续执行。如果这是正确的(我是新手,不太清楚),则如果通道为空但尚未关闭,
if ok
行将计算为
false
,并执行
else
分支。但是如果要替换“v,ok:=您在哪里读到该操作未阻塞?我找不到它,它似乎与我观察到的不匹配。我从文档中读到,通道关闭后它不会阻塞。这似乎来自较旧版本的规范,例如,查看“方法表达式”之前的最后一段。”.在当前版本中,这一段稍作更改,并表示“由于通道关闭且为空,因此返回的值为零(false)”。这听起来像是
false
只有在通道排空并关闭后才会返回,对吗?这意味着我弄错了。不,不完全是。我要寻找的是带有签名的函数
func multiplex(cin[]chan int,cout chan int)
,即可以在任意数量的输入通道上操作而不是硬编码为两个的通道。现在有一个带有函数()的包,它使用反射而不是多个goroutine来解决问题。
func combine(inputs []<-chan int, output chan<- int) {
  var group sync.WaitGroup
  for i := range inputs {
    group.Add(1)
    go func(input <-chan int) {
      for val := range input {
        output <- val
      }
      group.Done()
    } (inputs[i])
  }
  go func() {
    group.Wait()
    close(output)
  } ()
}