【问题标题】:Why is nested goroutine necessary?为什么需要嵌套 goroutine?
【发布时间】:2017-12-15 06:52:13
【问题描述】:

我正在研究 MIT 6.824 - lab1(第三部分),但对(迷你)mapreduce 的一个调度程序感到困惑:

var wg sync.WaitGroup
for i := 0; i < ntasks; i++ {
    task_arg := DoTaskArgs{ JobName: jobName, File: mapFiles[i], Phase: phase, TaskNumber: i, NumOtherPhase: n_other }
    //not so relevant

    wg.Add(1)
    go func() {
        defer wg.Done()
        reg_worker := <- registerChan
        call(reg_worker, "Worker.DoTask", task_arg, nil)
        go func() { registerChan <- reg_worker }()
        //registerChan <- reg_worker
    }()
}
wg.Wait()

这个程序

  1. 从频道registerChan 将 100 个任务部署到 2 个工作人员(使用call
  2. 任务完成后将工作人员放回频道
  3. wg.Add()wg.Donewg.Wait() 同步

如何调用schedule (https://github.com/WentaoZero/mini-mapreduce/blob/master/master.go#L84):

ch := make(chan string)
go mr.forwardRegistrations(ch)
schedule(mr.jobName, mr.files, mr.nReduce, phase, ch)

registerChan 未缓冲。

我尝试在go func() { registerChan &lt;- reg_worker }() 行删除goroutine 以使其成为:registerChan &lt;- reg_worker

在完成 50 多个任务后程序卡住。我想它证明了 goroutine 正在工作,但我不明白它为什么会卡住。

registerChan &lt;- reg_worker已经写在一个goroutine里了,为什么还要用另一个goroutine来包装呢?

我不认为系统的其余部分是相关的,所以我不会在这里发布。如果需要,您可以查看https://github.com/WentaoZero/mini-mapreduce。此调度程序选自https://github.com/WentaoZero/mini-mapreduce/blob/master/schedule.go

【问题讨论】:

  • reg_worker :=
  • @sahaj 我确信已经完成了 50 多个任务。
  • 包含的代码中没有任何内容表明为什么它应该完成 50 多个任务并在完成所有 100 个任务之前卡住。原因应该在代码的其余部分中,这使得这个话题离题了。
  • registerChan 缓冲了吗?
  • @britt 不,不是。只需添加一些解释即可。

标签: go goroutine


【解决方案1】:

内部的 goroutine 是非常必要的,因为 registerChan 没有被缓冲!

发送到无缓冲的通道会被阻止,除非它被接收到。在您的情况下,您有 100 个任务,这意味着 reg_worker := &lt;- registerChanregisterChan &lt;- reg_worker 都将被调用 100 次。似乎发送/接收是平衡的,对吧?

但是,您仍然有 2 个工人!你的代码中一定有类似registerChan &lt;- new_worker的东西(很可能是mr.forwardRegistrations(ch)),它应该被调用2次!

因此,在无缓冲通道上发送比接收多两个,因此最后两个 registerChan &lt;- reg_worker 被阻止,然后 defer wg.Done()wg.Wait() 也被阻止!

为了验证它,你可以这样调试:

        defer wg.Done()
        reg_worker := <-registerChan
        call(reg_worker, "Worker.DoTask", task_arg, nil)
        fmt.Println("### start sending to registerChan")
        registerChan <- reg_worker
        fmt.Println("### send done")

只计算这两个句子的出现次数。应该是 100 和 98!

【讨论】:

  • 我也在研究 6.824,我建议你把 wg.Done() 放在 registerChan &lt;- new_worker 之前,而不是使用内部 goroutine。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-24
  • 2012-08-28
  • 1970-01-01
  • 1970-01-01
  • 2020-01-12
  • 2010-11-08
  • 1970-01-01
相关资源
最近更新 更多