【问题标题】:looking for golang concurrency pattern寻找golang并发模式
【发布时间】:2013-08-30 08:55:12
【问题描述】:

这是我要解决的问题:

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 是单个 goroutines
  • 在任何时候都应该有恒定数量的 goroutine(因此不会为每个新数据项添加新的 goroutine)。

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

【问题讨论】:

  • 1) 如果您寻求大型代码示例的帮助:请考虑编写惯用的 Go 代码,例如account 而不是 account_chan(或至少 accountChan。2)您对通道的使用非常奇怪:当普通函数或 goroutine 调用足够时通过通道传递整数看起来很奇怪。 3)你问并发;但你的意思是并行性吗? 4) 根据物理内核的数量,您的工作 goroutine 可能永远不会并行运行。 5) 缓冲你的 wa_{in,out} 频道?
  • @Volker 1)这是当前项目中使用的命名约定,所以我自然而然地想到了,2)这是一个概念验证示例,在我的真实示例中,我正在传递结构(不是整数)3)我从并发中受益(例如,workerA 通过网络执行某些操作,workerB 写入 fs)4)看看 3 5)没有解决我的问题。
  • 您是否正在寻找一种解决方案,一个工人可以完成所有项目,而第二个工人仍在处理第一个。所有这些都要求在final_chan 上都没有发送任何东西,直到双方都完成了特定的项目?如果workerA 先完成,它可以帮助workerB 处理它的项目吗?
  • @deft 代码是,是,不是每个人都只能做自己的工作。

标签: concurrency go


【解决方案1】:

根据您提供的限制,您无能为力。简单地重新排序通道操作以允许并发可能就是您所寻找的。​​p>

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

    <-wa_out
    <-wb_out

    final_chan <- d
}

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


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

【讨论】:

  • 是的,从不使用 worker out chan 值(它们仅用于表示完成)。在此示例中,我已将类型更改为 chan bool:play.golang.org/p/Gua-raGaqb
  • 我尝试使用等待组来实现这一点,例如:play.golang.org/p/aFRfQ78Wu7。不知何故,它对我来说看起来更干净,但我更喜欢第一个选项(因为我不会为每个数据项生成 goroutine。是的,任何时候只有 2 个,但仍然......)
【解决方案2】:

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

// 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

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

另见the concurrency patterns go doc

【讨论】:

    猜你喜欢
    • 2017-08-17
    • 2022-06-16
    • 1970-01-01
    • 1970-01-01
    • 2014-12-29
    • 1970-01-01
    • 2019-06-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多