1

这是我要解决的问题:

package main

import "fmt"

func workerA(work_in_chan <-chan int,work_out_chan chan<- int){
   for d := range work_in_chan {
    fmt.Println("A ",d)
    work_out_chan <- d
   }
}

func workerB(work_in_chan <-chan int,work_out_chan chan<- int){
   for d := range work_in_chan {
    fmt.Println("B ",d)
    work_out_chan <- d
   }
}

func account(account_chan <-chan int,final_chan chan<- int){

    wa_in := make(chan int)
    wa_out := make(chan int)
    wb_in := make(chan int)
    wb_out := make(chan int)

    go workerA(wa_in,wa_out)
    go workerB(wb_in,wb_out)

    for d := range account_chan {

        //TODO - dumb implementation starts here
        wa_in <- d
        <-wa_out

        wb_in <- d
        <-wb_out
        //TODO - dumb implementation ends here

        final_chan <- d
    }
}

func main() {

    account_chan := make(chan int, 100)
    final_chan := make(chan int, 100)

    go account(account_chan,final_chan)

    account_chan <- 1
    account_chan <- 2
    account_chan <- 3

    fmt.Println(<-final_chan)
    fmt.Println(<-final_chan)
    fmt.Println(<-final_chan)
}

account goroutine 接收 account_chan 上的传入数据,对数据执行一些工作,一旦完成,将数据发送到 final_chan。账户工作由 workerA 和 workerB 完成(顺序不重要),两者都必须在账户将数据发送到 final_data 之前完成。有几个要求:

  • workerA 和 workerB 是单个 goroutine
  • 任何时候都应该有恒定数量的 goroutine(所以不要为每个新数据项添加新的 goroutine)。

我粘贴的实现是愚蠢的,因为现在 workerA 和 workerB 永远不会同时执行(因为它们可以并且应该因为它们完全相互独立)。那么我可以使用哪种并发模式来解决这个问题呢?

4

2 回答 2

1

您将输入传递给工作人员,然后阻止,直到您分别获得他们的结果。

// Give worker A work
wa_in <- d
// Wait until worker A finished
<-wa_out

// Give worker B work
wb_in <- d
// Wait until worker B finished
<-wb_out

相反,使用select语句同时在两个通道之一上等待结果:

func account(account_chan <-chan int,final_chan chan<- int){

    wa_in := make(chan int)
    wa_out := make(chan int)
    wb_in := make(chan int)
    wb_out := make(chan int)

    go workerA(wa_in,wa_out)
    go workerB(wb_in,wb_out)

    for d := range account_chan {
        wa_in <- d
        wb_in <- d
        for i := 0 ; i < 2; i++ {
            select {
                case <-wa_out:
                case <-wb_out:
            }
        }
        final_chan <- d
    }
}

http://play.golang.org/p/U0fk1yiqWL

现在,两个工人将同时运行,但程序仍保证等待所有工人完成。

另请参阅并发模式 go doc

于 2013-08-29T18:53:37.503 回答
1

有了你提供的限制,没有什么可以做的。简单地重新排序通道操作以允许并发可能就是您所寻找的。

for d := range account_chan {
    wa_in <- d
    wb_in <- d

    <-wa_out
    <-wb_out

    final_chan <- d
}

play.golang.org/p/4d8hKyHTWq
我第一次看到这种模式时,我担心“但是如果 B 先完成怎么办”。事实证明,顺序并不重要,因为两者都需要从中接收。


风格的旁白:
提供的代码片段闻起来像它有太多的通道和 goroutines。但这可能是因为这是一个更复杂的问题,被提炼为基本部分。实际上可能是一个问题的一件事是工人的输出通道。他们的输出没有在示例中使用,我看不出它是如何出现在完整列表中的。要么复制值,在这种情况下不需要输出通道(async.WaitGroup会更好),要么在工作人员之间共享它们不安全。

于 2013-08-30T00:55:12.343 回答