简体   繁体   English

为什么这个 goroutine 不调用 wg.Done()?

[英]Why does this goroutine not call wg.Done()?

Suppose there are a maximum two elements (worker addresses) on registerChan at any point.假设任何时候 registerChan 上最多有两个元素(工作地址)。 Then for some reason, the following code does not call wg.Done() in the last two goroutines.然后由于某种原因,以下代码在最后两个 goroutine 中没有调用 wg.Done()。

func schedule(jobName string, mapFiles []string, nReduce int, phase jobPhase, registerChan chan string) {
    var ntasks int
    var nOther int // number of inputs (for reduce) or outputs (for map)
    switch phase {
    case mapPhase:
        ntasks = len(mapFiles)
        nOther = nReduce
    case reducePhase:
        ntasks = nReduce
        nOther = len(mapFiles)
    }

    fmt.Printf("Schedule: %v %v tasks (%d I/Os)\n", ntasks, phase, nOther)

    const rpcname = "Worker.DoTask"
    var wg sync.WaitGroup
    for taskNumber := 0; taskNumber < ntasks; taskNumber++ {
        file := mapFiles[taskNumber%len(mapFiles)]
        taskArgs := DoTaskArgs{jobName, file, phase, taskNumber, nOther}
        wg.Add(1)
        go func(taskArgs DoTaskArgs) {
            workerAddr := <-registerChan
            print("hello\n")
            // _ = call(workerAddr, rpcname, taskArgs, nil)
            registerChan <- workerAddr
            wg.Done()
        }(taskArgs)
    }
    wg.Wait()
    fmt.Printf("Schedule: %v done\n", phase)
}

If I put wg.Done() before registerChan <- workerAddr it works just fine and I have no idea why.如果我将wg.Done()放在registerChan <- workerAddr之前,它工作得很好,我不知道为什么。 I have also tried deferring wg.Done() but that doesn't seem to work even though I expected it to.我也尝试过推迟 wg.Done() ,但即使我期望它似乎也不起作用。 I think I have some misunderstanding of how go routines and channels work which is causing my confusion.我想我对 go 例程和通道的工作方式有一些误解,这导致了我的困惑。

Because it stopped here:因为它在这里:

workerAddr := <-registerChan

For a buffered channel:对于缓冲通道:
To get this workerAddr:= <-registerChan to work: the channel registerChan must have a value;要让这个workerAddr:= <-registerChan工作:通道registerChan必须有一个值; otherwise, the code will stop here waiting for the channel .否则,代码将在此处停止等待通道


I managed to run your code this way (try this ):我设法以这种方式运行您的代码(试试这个):

package main

import (
    "fmt"
    "sync"
)

func main() {
    registerChan := make(chan int, 1)
    for i := 1; i <= 10; i++ {
        wg.Add(1)
        go fn(i, registerChan)
    }
    registerChan <- 0 // seed
    wg.Wait()
    fmt.Println(<-registerChan)
}

func fn(taskArgs int, registerChan chan int) {
    workerAddr := <-registerChan
    workerAddr += taskArgs
    registerChan <- workerAddr
    wg.Done()
}

var wg sync.WaitGroup

Output: Output:

55

Explanation:解释:
This code adds 1 to 10 using a channel and 10 goroutines plus one main goroutine.这段代码使用一个通道和 10 个 goroutine 加上一个主 goroutine 将 1 加到 10。

I hope this helps.我希望这有帮助。

When you run this statement registerChan <- workerAddr , if the channel capacity is full you cannot add in it and it will block.当您运行此语句registerChan <- workerAddr时,如果通道容量已满,您将无法添加它并且它将阻塞。 If you have a pool of, say 10, workerAddr, you could add all of them in a buffered channel of capacity 10 before calling schedule .如果您有一个池,例如 10 个 workerAddr,您可以在调用schedule之前将它们全部添加到容量为 10 的缓冲通道中。 Do not add after the call, to guarantee that if you take a value from the channel, there is space to add it again after.调用后不要添加,以保证如果从通道中取值,之后有空间再次添加。 Using defer at the beginning of your goroutine is good.在你的 goroutine 的开头使用defer是好的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM