作为测试的一部分,我有以下代码:
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()
我的疑虑是:
- 到目前为止,我看到的很多代码都在goroutine中执行
wg.Add(1)
。为什么它在这种特殊情况下会表现得出乎意料?这里似乎发生的事情是,一些goroutines似乎完成了运行,并通过了wg。Wait((,在其他goroutine开始运行之前。正在使用wg。在goroutine中添加(1(是否危险/应避免?如果这不是一个普遍的问题,那么到底是什么导致了这个问题 - 添加
wg.Add(expected)
是否是解决此问题的正确方法
根据文档-
WaitGroup等待goroutines的集合完成。主要goroutine调用Add来设置要等待的goroutine数。然后每个goroutine都会运行,并在完成时调用Done。同时时间,等待可以用来阻止,直到所有goroutine都完成。
因此Add()
必须由一个goroutine调用,该goroutine正在启动其他goroutine,在您的情况下是main
goroutine。
在第一个代码片段中,您在其他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()
第二个代码段正在工作,因为您正在main
goroutine-中调用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
块之外