如何(或者我应该)在Wait()期间防止Go WaitGroup调用Add()函数?

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

How to (or should I) prevent Go WaitGroup calls Add() during Wait()?

问题

假设我有两个goroutine,一个在另一个中运行,每个都需要一个waitgroup来防止goroutine泄漏。以下是代码:

func A(wg *sync.WaitGroup) {
	defer wg.Done()
	// 做一些事情
}

func B(wg *sync.WaitGroup) {
	defer wg.Done()
	wg.Add(1)  // <- 这是给A()准备的
	go A(wg)
	// 做一些事情
}

func main() {
	wg := &sync.WaitGroup{}
    wg.Add(1)  // <- 这是给B()准备的
	go B(wg)
	// 做一些事情
	wg.Wait()
}

这段代码运行良好。但是在B()函数内部的wg.Add(1)行在main()函数中的wg.Wait()开始之后调用的情况是可能发生的。尽管在这种情况下代码仍然可以正常工作,但这似乎是一个潜在的竞态条件。

请注意,这并不违反WaitGroup规则:“当计数器为零时,对计数器进行正增量的调用必须在等待之前发生”。在这里,B()内部的Add()是在计数器为1时发生的。

我的问题是:在一个goroutine中调用wg.Add()时,另一个goroutine中的wg.Wait()正在阻塞,这样做是否可以?如果不可以,这里有什么最佳实践?显然,我可以选择不共享一个waitgroup给AB,而是分别为它们使用2个waitgroup,但这会变得非常混乱,特别是当有更多的函数时,我们最终会有很多waitgroup需要等待。

英文:

Let's say I have 2 goroutines, one running in another, each needs a waitgroup to prevent goroutine leak. Here is the code:

func A(wg *sync.WaitGroup) {
	defer wg.Done()
	// do something
}

func B(wg *sync.WaitGroup) {
	defer wg.Done()
	wg.Add(1)  // &lt;- this is for A()
	go A(wg)
	// do something
}

func main() {
	wg := &amp;sync.WaitGroup{}
    wg.Add(1)  // &lt;- this is for B()
	go B(wg)
	// do something
	wg.Wait()
}

It works well. But it is possible that the wg.Add(1) line inside B() is called after the wg.Wait() in main() started. The code is still working well in that case. But this seems to be a potential race condition to me.

Notice that this is not violating the WaitGroup rule: "calls with a positive delta that occur when the counter is zero must happen before a Wait". Here the Add() inside B() is happening when the counter is 1.

My question is: is it ok to call wg.Add() in one goroutine when wg.Wait() is blocking in another goroutine? If not, what's the best practice here? Apparently I can choose to not share one wairgroup for both A and B, instead to use 2 waitgroups for them respectively, but it will be pretty messy, especially when there are even more functions and we end up with a lot waitgroups to wait.

答案1

得分: 4

在另一个goroutine正在等待wg.Wait的时候调用wg.Add是可以的。这不会产生竞争条件,并且只要在调用wg.Add之前wg没有达到零,它就能正常工作,因为这会释放wg.Wait。在你的用法中,wg不可能达到零,因为即使goroutine BA开始之前终止,wg也不会达到零,因为它是在新的goroutine开始之前添加的。

英文:

It is ok to call wg.Add while another goroutine is waiting on wg.Wait. There are no race conditions, and it will work fine as long as wg does not reach zero before wg.Add is called, because that will release wg.Wait. In your usage, there is no way wg can reach zero, because even if goroutine B terminates before A starts, wg would not reach zero because it is added before the new goroutine starts.

huangapple
  • 本文由 发表于 2023年5月12日 01:29:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/76230217.html
匿名

发表评论

匿名网友

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

确定