英文:
Go Select and multiple cases at once
问题
如何将下面的代码修改为在接收到C1和C2时执行某些操作?
import "time"
import "fmt"
func main() {
    c1 := make(chan string)
    c2 := make(chan string)
    go func() {
        time.Sleep(time.Second * 1)
        c1 <- "one"
    }()
    go func() {
        time.Sleep(time.Second * 2)
        c2 <- "two"
    }()
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println("received", msg1)
        case msg2 := <-c2:
            fmt.Println("received", msg2)
        }
    }
}
将代码修改为在接收到C1和C2时执行某些操作,可以使用一个额外的goroutine来处理这个逻辑。修改后的代码如下所示:
import "time"
import "fmt"
func main() {
    c1 := make(chan string)
    c2 := make(chan string)
    done := make(chan bool) // 用于通知操作完成的通道
    go func() {
        time.Sleep(time.Second * 1)
        c1 <- "one"
    }()
    go func() {
        time.Sleep(time.Second * 2)
        c2 <- "two"
    }()
    go func() {
        for i := 0; i < 2; i++ {
            select {
            case msg1 := <-c1:
                fmt.Println("received", msg1)
            case msg2 := <-c2:
                fmt.Println("received", msg2)
            }
        }
        done <- true // 所有操作完成后发送通知
    }()
    <-done // 等待操作完成
}
这样修改后的代码会在接收到C1和C2时执行相应的操作,并在所有操作完成后结束程序。
英文:
How to adapt the code below to do something when C1 and C2 have been BOTH received
https://gobyexample.com/select
import "time"
import "fmt"
func main() {
    c1 := make(chan string)
    c2 := make(chan string)
    go func() {
        time.Sleep(time.Second * 1)
        c1 <- "one"
    }()
    go func() {
        time.Sleep(time.Second * 2)
        c2 <- "two"
    }()
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-c1:
            fmt.Println("received", msg1)
        case msg2 := <-c2:
            fmt.Println("received", msg2)
        }
    }
}
答案1
得分: 5
这可能是一种称为"fan-in"的管道技术:
一个函数可以从多个输入中读取,并在所有输入关闭时通过将输入通道多路复用到一个单一通道上来继续执行,当所有输入都关闭时,该通道也关闭。这被称为"fan-in"。
func merge(cs ...<-chan int) <-chan int {
    var wg sync.WaitGroup
    out := make(chan string)
    // 为cs中的每个输入通道启动一个输出goroutine。output将值从c复制到out,直到c关闭或从done接收到一个值,然后output调用wg.Done。
    output := func(c <-chan string) {
        for n := range c {
            select {
            case out <- "received " + n:
            case <-done:
            }
        }
        wg.Done()
    }
    wg.Add(len(cs))
    for _, c := range cs {
        go output(c)
    }
    // 启动一个goroutine,在所有输出goroutine完成后关闭out。这必须在wg.Add调用之后启动。
    go func() {
        wg.Wait()
        close(out)
    }()
    return out
}
在这个playground中可以看到一个完整的示例。
注意:无论你最终使用什么解决方案,都可以阅读一下这篇文章:
使用通道设计Go API的原则,作者是Alan Shreve。
特别是:
原则 #1
API应该声明其通道的方向性。
英文:
That could be a pipeline technique, called fan-in:
> A function can read from multiple inputs and proceed until all are closed by multiplexing the input channels onto a single channel that's closed when all the inputs are closed. This is called fan-in.
func merge(cs ...<-chan int) <-chan int {
    var wg sync.WaitGroup
    out := make(chan string)
    // Start an output goroutine for each input channel in cs.  output
    // copies values from c to out until c is closed or it receives a value
    // from done, then output calls wg.Done.
    output := func(c <-chan string) {
        for n := range c {
            select {
            case out <- "received " + n:
            case <-done:
            }
        }
        wg.Done()
    }
    wg.Add(len(cs))
    for _, c := range cs {
        go output(c)
    }
    // Start a goroutine to close out once all the output goroutines are
    // done.  This must start after the wg.Add call.
    go func() {
        wg.Wait()
        close(out)
    }()
    return out
}
See a complete example in this </kbd>playground</kbd>
Note: whatever solution you will end up using, a good read remains:
Principles of designing Go APIs with channels by Alan Shreve.
In particular:
Principle #1
> An API should declare the directionality of its channels.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论