我编写了的第一个实现
sync.WaitGroup,并且很好地支持了这种情况和其他极端情况。从那以后,德米特里(Dmitry)改进了实施方式,鉴于他的往绩,我敢打赌他只会使它更安全。
特别是,您可以相信,如果当前有一个或多个被阻止的
Wait呼叫,然后在呼叫
Add之前以正增量进行呼叫
Done,则不会取消阻止任何先前存在的
Wait呼叫。
因此,您绝对可以这样做,例如:
var wg sync.WaitGroupwg.Add(1)go func() { wg.Add(1) go func() { wg.Done() }() wg.Done()}()wg.Wait()自从代码首次集成以来,我实际上在生产中使用了等效逻辑。
作为参考,此内部注释已在第一个实现中提出,并且仍然存在:
// WaitGroup creates a new semaphore each time the old semaphore// is released. This is to avoid the following race://// G1: Add(1)// G1: go G2()// G1: Wait() // Context switch after Unlock() and before Semacquire().// G2: Done() // Release semaphore: sema == 1, waiters == 0. G1 doesn't run yet.// G3: Wait() // Finds counter == 0, waiters == 0, doesn't block.// G3: Add(1) // Makes counter == 1, waiters == 0.// G3: go G4()// G3: Wait() // G1 still hasn't run, G3 finds sema == 1, unblocked! Bug.
这描述了在接触实现时要记住的另一种竞争条件,但请注意,即使使用
G1进行
Add(1) + go f()竞争,该模式也是如此
G3。
不过,我理解您的问题,因为最近发布的文档中确实有一个令人困惑的陈述,但是让我们查看评论的历史以了解其实际解决的内容。
Russ在修订版15683中发表了评论:
(...)+// Note that calls with positive delta must happen before the call to Wait,+// or else Wait may wait for too small a group. Typically this means the calls+// to Add should execute before the statement creating the goroutine or+// other event to be waited for. See the WaitGroup example.func (wg *WaitGroup) Add(delta int) {拉斯的日志注释指出:
同步:注意在哪里打电话(* WaitGroup)。
修复了问题4762。
如果阅读问题4762,则会发现:
可能值得在sync.WaitGroup文档中添加明确的注释,即在启动包含对Done的调用的go例程之前,应完成对Add的调用。
因此,该文档实际上是针对这样的代码发出警告:
var wg sync.WaitGroupwg.Add(1)go func() { go func() { wg.Add(1) wg.Done() }() wg.Done()}()wg.Wait()这确实是坏的。只是应该对评论进行改进,使其更加具体,并避免您在阅读过程中所产生的似是而非但令人误解的理解。



