寻找golang并发模式

时间:2013-08-29 15:29:10

标签: concurrency go

以下是我要解决的问题:

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)
}

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

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

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

2 个答案:

答案 0 :(得分: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

现在,两名工人将同时运作,但该计划仍然保证等待所有工人完成。

另见the concurrency patterns go doc

答案 1 :(得分: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。但这可能是因为这是一个更复杂的问题,精炼到了必要的部分。实际上可能存在的一个问题是来自工人的渠道。它们的输出未在示例中使用,我无法看到它在完整列表中的表现。要么复制值,在这种情况下不需要输出通道(sync.WaitGroup会更好),或者在工人之间共享它们是不安全的。