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

为什么Go函数中的go func需要等待组才能正确退出?

为什么Go函数中的go func需要等待组才能正确退出?

Go
蝴蝶刀刀 2022-09-05 09:26:58
Sry这个标题可能会产生误导。实际上,完整的代码如下:package mainimport (    "fmt"    "sync")type Button struct {    Clicked *sync.Cond}func main() {    button := Button{        Clicked: sync.NewCond(&sync.Mutex{}),    }    subscribe := func(c *sync.Cond, fn func()) {        var wg sync.WaitGroup        wg.Add(1)        go func() {            wg.Done()            c.L.Lock()            defer c.L.Unlock()            c.Wait()            fn()        }()        wg.Wait()    }    var clickRegistered sync.WaitGroup    clickRegistered.Add(2)    subscribe(button.Clicked, func() {        fmt.Println("maximizing window")        clickRegistered.Done()    })    subscribe(button.Clicked, func() {        fmt.Println("displaying dialog")        clickRegistered.Done()    })    button.Clicked.Broadcast()    clickRegistered.Wait()}当我注释一些行并再次运行它时,它会抛出一个致命错误订阅函数更改如下所示:"all goroutines are asleep - deadlock!"subscribe := func(c *sync.Cond, fn func()) {        //var wg sync.WaitGroup        //wg.Add(1)        go func() {            //wg.Done()            c.L.Lock()            defer c.L.Unlock()            c.Wait()            fn()        }()        //wg.Wait()    }让我感到困惑的是,是否在外部函数返回之前执行。在我看来,尽管外部函数已返回,但 will 将作为守护程序运行,因此该变量是不必要的。但这表明我完全错了。因此,如果 有可能不被调度,这是否意味着我们必须在每个函数或代码块中使用 in,以确保在函数或代码块返回之前调度 goroutine 执行?谢谢大家。go funcsubscribego funcwggo funcsync.WaitGroup
查看完整描述

2 回答

?
拉风的咖菲猫

TA贡献1995条经验 获得超2个赞

问题是,在任何一个调用中都不能保证在之前运行;甚至你的原始代码的使用也不能保证它(因为它是重要的部分,而不是goroutine的生成)c.Wait()button.Clicked.Broadcast()WaitGroupc.Wait()


修改订阅:


subscribe := func(c *sync.Cond, subWG *sync.WaitGroup, fn func()) {

        go func() {

            c.L.Lock()

            defer c.L.Unlock()

            subWG.Done() // [2]

            c.Wait()

            fn()

        }()

    }

等待代码:


subWG.Done()

button.Clicked.L.Lock()

button.Clicked.L.Unlock()

这是基于这样的观察,这种观察只能在开始时发生,或者发生在所有先前执行的goroutines都坚持之后,由于它们共享的储物柜。因此,这意味着(或订阅数量)被执行,只有一个goroutine没有坚持下去,这可以通过要求将储物柜到另一个时间来解决。[2][2]c.WaitsubWG.Wait()2[2]c.WaitLock


游乐场: https://play.golang.org/p/6mjUEcn3ec5


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

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

使用等待组(在当前组中编码):当函数返回时,您知道等待的goroutine至少已经开始执行。wgsubscribe

因此,当您的主要功能达到时,很有可能两个goroutine实际上正在等待他们的呼叫。button.Clicked.Broadcast()button.Clicked.Wait()

如果没有 ,则无法保证 goroutines 甚至已启动,并且您的代码可能调用得太快。wgbutton.Clicked.Broadcast()


请注意,使用 它只是降低了死锁发生的可能性,但它不会在所有情况下阻止死锁。wg

尝试编译你的二进制文件,并在循环中运行它(例如从bash :),我想你会看到同样的问题有时会发生。-racefor i in {1..100}; do ./myprogram; done


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

添加回答

举报

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