为什么需要嵌套goroutine?

I'm working on MIT 6.824 - lab1(part III) and get confused at a piece of scheduling program of a (mini) 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()

This program

  1. deploy 100 tasks to 2 worker (with call) from channel registerChan
  2. put worker back to channel when task finished
  3. wg.Add(), wg.Done, wg.Wait() to sync

how schedule is called (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 is not buffered.

I try removing goroutine at line go func() { registerChan <- reg_worker }() to make it: registerChan <- reg_worker

the program gets stuck after more than 50 tasks are done. I suppose it proves that the goroutine is working but I don't see why it gets stuck.

registerChan <- reg_worker has been written in a goroutine, why is it necessary to wrap it with another goroutine?

I don't think the rest of the system is relevant so I won't post it here. You may check https://github.com/WentaoZero/mini-mapreduce if needed. This scheduling program is picked from https://github.com/WentaoZero/mini-mapreduce/blob/master/schedule.go

the inner goroutine is quite nessesary since the registerChan is not buffered!

Sending to unbuffered channel is blocked unless it's been received. In your case, you got 100 tasks, which means that both reg_worker := <- registerChan and registerChan <- reg_worker will be called 100 times. Seems that sending/receiving is balanced, right?

However, you still got 2 workers! There must be something like registerChan <- new_worker somewhere in your code(most likely mr.forwardRegistrations(ch)), and it should be called 2 times!

Therefore, you have two more sending than receiving on a unbuffered channel, so the last 2 registerChan <- reg_worker are blocked, then defer wg.Done() and wg.Wait() are blocked as well!

To verify it, you could debug like this:

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

just count the occurrence number of these two sentences. It should be 100 and 98!