简体   繁体   中英

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

Suppose there are a maximum two elements (worker addresses) on registerChan at any point. Then for some reason, the following code does not call wg.Done() in the last two goroutines.

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. I have also tried deferring wg.Done() but that doesn't seem to work even though I expected it to. I think I have some misunderstanding of how go routines and channels work which is causing my confusion.

Because it stopped here:

workerAddr := <-registerChan

For a buffered channel:
To get this workerAddr:= <-registerChan to work: the channel registerChan must have a value; 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:

55

Explanation:
This code adds 1 to 10 using a channel and 10 goroutines plus one main goroutine.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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