同步.在goroutine启动之前进行WaitGroup初始化



作为测试的一部分,我有以下代码:

expected := 10
var wg sync.WaitGroup
for i := 0; i < expected; i++ {
go func(wg *sync.WaitGroup) {
wg.Add(1)
defer wg.Done()
// do something
}(&wg)
}
wg.Wait()

让我惊讶的是,我在跑步时得到了panic: Fail in goroutine after TestReadWrite has completed;进行测试";。当使用";进行测试赛";,我没有感到恐慌,但后来考试失败了。在这两种情况下,尽管有一个wg。Wait((,goroutine没有完成执行。

我做了以下更改,现在测试工作如预期:

expected := 10
var wg sync.WaitGroup
wg.Add(expected)
for i := 0; i < expected; i++ {
go func(wg *sync.WaitGroup) {
defer wg.Done()
// do something
}(&wg)
}
wg.Wait()

我的疑虑是:

  1. 到目前为止,我看到的很多代码都在goroutine中执行wg.Add(1)。为什么它在这种特殊情况下会表现得出乎意料?这里似乎发生的事情是,一些goroutines似乎完成了运行,并通过了wg。Wait((,在其他goroutine开始运行之前。正在使用wg。在goroutine中添加(1(是否危险/应避免?如果这不是一个普遍的问题,那么到底是什么导致了这个问题
  2. 添加wg.Add(expected)是否是解决此问题的正确方法

根据文档-

WaitGroup等待goroutines的集合完成。主要goroutine调用Add来设置要等待的goroutine数。然后每个goroutine都会运行,并在完成时调用Done。同时时间,等待可以用来阻止,直到所有goroutine都完成。

因此Add()必须由一个goroutine调用,该goroutine正在启动其他goroutine,在您的情况下是maingoroutine。

在第一个代码片段中,您在其他goroutine中调用Add(),而不是导致问题的maingoroutine-

expected := 10
var wg sync.WaitGroup
for i := 0; i < expected; i++ {
go func(wg *sync.WaitGroup) {
wg.Add(1) // Do not call Add() here
defer wg.Done()
// do something
}(&wg)
}
wg.Wait()

第二个代码段正在工作,因为您正在maingoroutine-中调用Add()

expected := 10
var wg sync.WaitGroup
wg.Add(expected) // Okay
for i := 0; i < expected; i++ {
go func(wg *sync.WaitGroup) {
defer wg.Done()
// do something
}(&wg)
}
wg.Wait()

正在添加wg。添加(预期(解决此问题的正确方法?

您也可以在for循环-中调用wg.Add(1)

expected := 10
var wg sync.WaitGroup
for i := 0; i < expected; i++ {
wg.Add(1) // Okay
go func(wg *sync.WaitGroup) {
defer wg.Done()
// do something
}(&wg)
}
wg.Wait()

你的第一种方法很恐慌,因为(WaitGroup.Add(:

Add将可能为负的delta添加到WaitGroup计数器。如果计数器变为零,所有在Wait上被阻止的goroutine都被释放。如果计数器为负数,Add会恐慌。

通常,这意味着Add应在创建goroutine或等待的其他事件

当在代码末尾调用Wait((时,可能还没有任何goroutine开始执行,因此WaitGroup中的值为0。稍后,当你的go例程执行时,调用go例程已经被释放。这会导致意想不到的行为,在你的情况下会引起恐慌。也许你使用了调用go例程中的值。

你的第二种方法非常好。您也可以在循环内调用.Add(1),但在go func块之外

最新更新