为了账号安全,请及时绑定邮箱和手机立即绑定

同步。在戈鲁丁开始之前等待组初始化

同步。在戈鲁丁开始之前等待组初始化

Go
汪汪一只猫 2022-09-12 21:16:08
我在测试中提供了以下代码:    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()令我惊讶的是,我在运行“去测试”时得到了。当用“去测试-比赛”跑步时,我没有感到恐慌,但测试在稍后的某个时刻失败了。在这两种情况下,尽管有一个wg。Wait(),一个戈鲁廷没有完成执行。panic: Fail in goroutine after TestReadWrite has completed我做了以下更改,现在测试按预期工作:    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()我的疑虑是:到目前为止,我看到的很多代码都在戈鲁廷内部。为什么它在这个特定情况下会表现得出乎意料?这里似乎正在发生的事情是,一些戈鲁丁似乎完成了运行,并通过了wg。等候(),在其他戈鲁廷开始运行之前。正在使用工作组。添加(1)在戈鲁丁内危险/要避免?如果这通常不是问题,那么究竟是什么导致了这里的问题?wg.Add(1)添加解决此问题的正确方法是否正确?wg.Add(expected)
查看完整描述

2 回答

?
潇湘沐

TA贡献1816条经验 获得超6个赞

根据文档 -


等待组等待一组大流子体完成。主戈鲁丁调用 Add 以设置要等待的戈鲁廷数。然后,每个戈鲁丁都会运行并在完成后调用“完成”。同时,等待可用于阻止,直到所有戈鲁丁都完成。


所以必须由一个戈鲁廷来调用,它正在引发其他的戈鲁廷,在你的情况下是戈鲁廷。Add()main


在第一个代码片段中,您正在调用其他戈鲁廷内部,而不是导致问题的主要戈鲁廷 -Add()


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()

第二个片段正在工作,因为你正在调用戈鲁廷 -Add()main


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()

正在添加工作组。添加(预期)解决此问题的正确方法?


您也可以在 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()


查看完整回答
反对 回复 2022-09-12
?
手掌心

TA贡献1942条经验 获得超3个赞

你的第一种方法恐慌 coz (WaitGroup.Add):

“添加”将增量(可能为负数)添加到“等待组”计数器。如果计数器变为零,则释放在 Wait 上阻止的所有大猩猩。如果计数器变为负数,则添加恐慌。

...

...

通常,这意味着对 Add 的调用应在创建 goroutine 或其他要等待的事件的语句之前执行

当在代码末尾调用 Wait() 时,可能没有一个 goroutine 开始执行 - 因此 WaitGroup 中保存的值为 0。然后,当您的 go 例程执行时,调用 go 例程已经发布。这将导致意想不到的行为,在您的案例中引起恐慌。也许你在那里使用了调用 go 例程中的值。

你的第二种方法绝对没问题。您也可以在环路内调用 - 但在块外.Add(1)go func


查看完整回答
反对 回复 2022-09-12
  • 2 回答
  • 0 关注
  • 76 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信