Sync.WaitGroup,为什么在goroutine中更近

Below is the example code in the Go programming book. I do not understand why the closer needs to be its own goroutine. I tried to move the closer into the main but it crashes. Somebody could explain why the closer needs to be in a separate goroutine?

Thanks!

func makeThumbnails(filenames <-chan string, result chan<- int64) int64 {
  sizes := make(chan int64)
  var wg sync.WaitGroup
  for f := range filenames {
      wg.Add(1)
      go func(f string) {
        defer wg.Done()
        sizes <- int64(len(f))
      }(f)
  }

  // **closer**, why this guy needs to be in a goroutine???
  go func() {
    wg.Wait()
    close(sizes)
  }()

  var total int64
  for size := range sizes {
    total += size
  }
  result <- total
  return total
}

The problem is that sizes is not a buffered chan, so only one of the anonymous goroutines can actually complete before sizes needs to be read from. That makes wg.Wait() wait forever (since the next goroutine is blocking on sizes <- and can't defer wg.Done()) and deadlocks.

By throwing the closer in a separate goroutine, it can close the sizes chan whenever it's ready to do so, and process from sizes in between. Ultimately this is a great use of a goroutine -- fire and forget closing!

To make this code work without the closer goroutine, you can simply initialize sizes as a buffered chan with a buffer >= the length of filenames.

func makeThumbnails(filenames <-chan string, result chan<- int64) int64 {
    sizes := make(chan int64, 10) // buffered channel, now!
    // if filenames sends more than 10 strings, though, we're in trouble!!

    var wg sync.WaitGroup
    for f := range filenames {
        wg.Add(1)
        go func(f string) {
            defer wg.Done()
            sizes <- int64(len(f))
        }(f)
    }

    // **closer**, this guy doesn't need to be a goroutine!!
    wg.Wait()
    close(sizes)

    var total int64
    for size := range sizes {
        total += size
    }
    result <- total
    return total
}

However since filenames's length is unknowable at runtime, it's not possible to do this easily. You'd have to read through filenames, store it into a slice, then initialize sizes and for over range filenamesSlice and.... yeah basically you've just re-written the whole function at that point.