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

wg.Add() 放在哪里

wg.Add() 放在哪里

Go
慕丝7291255 2022-07-18 16:54:15
var wg sync.WaitGroupvar v int32 = 0 for i = 0; i < 100; i++{   go func(){       wg.Add(1) // wrong place       atomic.AddInt32(&v,1)       wg.Done()   } }wg.Wait()fmt.Println(v)这是我从该视频中看到的一段代码https://subscription.packtpub.com/video/application_development/9781788994880/97598/97608/goroutines但是v总是小于100,我认为原因可能是wg.Wait()比预期更早结束,因为我们把wg.Add(1)匿名函数放在里面,并且在同一个 goroutinewg.Done()中会立即被调用,因此 main goroutine 从阻塞状态恢复执行。但是如果我们将wg.Add(1)放入 for 循环中, v 将永远是100。var wg sync.WaitGroupvar v int32 = 0 for i = 0; i < 100; i++{   wg.Add(1)   go func(){       atomic.AddInt32(&v,1)       wg.Done()   } }wg.Wait()fmt.Println(v)我的问题是为什么我们可以保证 main goroutine 总是会在这里阻塞,使得 v 最终等于 100。如果在 for 循环之前添加一个任务到wg, 并且 main goroutine 在这里恢复执行,那是否可能,因为此时没有任务。
查看完整描述

2 回答

?
慕田峪7331174

TA贡献1828条经验 获得超13个赞

wg.Add()在启动将调用的 goroutine 之前,您应该始终调用wg.Done().

在您更正的示例中,maingoroutine 只能wg.Wait()for循环之后到达,这可以保证您调用wg.Add()一百次,因此wg.Wait()会阻塞直到wg.Done()被调用100次数。

wg.Add()调用在新的 goroutine 中时,不能保证任何wg.Add()调用都会在maingoroutine 到达之前执行,wg.Wait()因为它们是并发运行的(在此之前没有同步)。这种情况下的行为是不确定的(取决于 goroutine 调度程序,它在没有显式同步的情况下是不确定的)。

请注意,如果您知道循环100进行迭代,另一种选择是wg.Add(100)在循环之前调用。我建议不要这样做,因为当循环包含breakcontinue操作时,这需要小心,这可能会导致启动的 goroutine 更少,从而最终你的maingoroutine 会卡住。是的,在您的情况下,这可能是微不足道的,但如果此代码及时发展,它可能会变得不那么明显,并可能导致未来的错误。当场景中涉及启动 goroutine 时,说它更快是无关紧要的。如果您只wg.Add(1)在启动 goroutine 之前调用,那么稍后您是否有条件地跳过这部分也没关系,因为您将在wg.Add()启动 goroutine 的同时跳过,并且您的代码将保持正确。

使用时要遵循的简单“规则” sync.WaitGroup:(引自this answer

  • 在语句之前调用WaitGroup.Add()“原始”goroutine(开始一个新的)go

  • 建议调用WaitGroup.Done()deferred,所以即使 goroutine 发生恐慌它也会被调用

  • 如果你想传递WaitGroup给其他函数(而不是使用包级变量),你必须传递一个指向它的指针,否则WaitGroup(这是一个结构)将被复制,并且Done()不会观察到在复制上调用的方法在原版上


查看完整回答
反对 回复 2022-07-18
?
吃鸡游戏

TA贡献1829条经验 获得超7个赞

就像其他人提到的那样wg.add(),应该在调用任何 go 例程之前调用。所以在主线程里面:


一开始就推迟是一种很好的做法,这样你就不会忘记,因为它被视为清理行动。


var wg sync.WaitGroup

var v int32 = 0 

for i = 0; i < 100; i++{

   wg.Add(1) //right place to put wg.add(1)

   go func(){

       defer wg.Done()

       atomic.AddInt32(&v,1)

   } 

}

wg.Wait()

fmt.Println(v)


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

添加回答

举报

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