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

Sync.WaitGroup, why closer in a goroutine

下面是Go编程书上的示例代码。我不明白为什么 closer 需要成为它自己的 goroutine。我试图将 closer 移动到 main 中,但它崩溃了。有人可以解释为什么 closer 需要在单独的 goroutine 中吗?

谢谢!

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
}

问题是 sizes 不是缓冲的 chan,因此在需要读取 sizes 之前,只有一个匿名 goroutines 可以实际完成。这使得 wg.Wait() 永远等待(因为下一个 goroutine 在 sizes <- 上阻塞并且不能 defer wg.Done())和死锁。

通过将 closer 放入单独的 goroutine 中,它可以在准备好时关闭 sizes chan,并在两者之间从 sizes 开始处理。归根结底,这是 goroutine 的一个很好的用途——启动后忘记关闭!

为了让这段代码在没有 closer goroutine 的情况下工作,你可以简单地将 sizes 初始化为一个缓冲区 >= filenames.

的长度的缓冲 chan
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
}

然而,由于 filenames 的长度在运行时是不可知的,所以不可能轻易做到这一点。您必须通读 filenames,将其存储到一个切片中,然后初始化大小和 for 超过 range filenamesSlice 和...是的,基本上您只是 re-written那时的整个功能。