Sync.WaitGroup, why closer in a goroutine

huangapple go评论70阅读模式
英文:

Sync.WaitGroup, why closer in a goroutine

问题

下面是《Go编程语言》一书中的示例代码。我不明白为什么closer需要成为它自己的goroutine。我尝试将closer移到主函数中,但程序会崩溃。有人可以解释一下为什么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,为什么这个函数需要在一个goroutine中?
  go func() {
    wg.Wait()
    close(sizes)
  }()

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

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 &lt;-chan string, result chan&lt;- 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 &lt;- 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 &lt;- total
  return total
}

答案1

得分: 4

问题在于sizes不是一个带缓冲的chan,所以只有一个匿名goroutine能在需要读取sizes之前完成。这使得wg.Wait()永远等待(因为下一个goroutine在sizes <-上阻塞,无法defer wg.Done()),从而导致死锁。

通过将关闭操作放在一个单独的goroutine中,它可以在准备好关闭sizes时关闭该通道,并在此期间从sizes中处理数据。从根本上讲,这是goroutine的一个很好的用法——启动并忘记关闭!

要使此代码在没有关闭goroutine的情况下工作,只需将sizes初始化为带有缓冲区的chan,缓冲区大小大于等于filenames的长度。

func makeThumbnails(filenames <-chan string, result chan<- int64) int64 {
    sizes := make(chan int64, 10) // 带缓冲的通道,现在!
    // 如果filenames发送的字符串超过10个,我们就有麻烦了!!

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

    // **关闭者**,这个人不需要成为一个goroutine!!
    wg.Wait()
    close(sizes)

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

然而,由于filenames的长度在运行时是不可知的,所以这样做并不容易。你需要遍历filenames,将其存储到一个切片中,然后初始化sizes并在range filenamesSlice上进行for循环... 是的,在这一点上,你基本上已经重写了整个函数。

英文:

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 &lt;- 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 &lt;-chan string, result chan&lt;- int64) int64 {
	sizes := make(chan int64, 10) // buffered channel, now!
    // if filenames sends more than 10 strings, though, we&#39;re in trouble!!

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

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

	var total int64
	for size := range sizes {
		total += size
	}
	result &lt;- 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.

huangapple
  • 本文由 发表于 2016年1月16日 03:52:12
  • 转载请务必保留本文链接:https://go.coder-hub.com/34818798.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定