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

Sry这个标题可能会产生误导。实际上,完整的代码如下:


package main


import (

    "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


蝴蝶刀刀
浏览 68回答 2
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

手掌心

使用等待组(在当前组中编码):当函数返回时,您知道等待的goroutine至少已经开始执行。wgsubscribe因此,当您的主要功能达到时,很有可能两个goroutine实际上正在等待他们的呼叫。button.Clicked.Broadcast()button.Clicked.Wait()如果没有 ,则无法保证 goroutines 甚至已启动,并且您的代码可能调用得太快。wgbutton.Clicked.Broadcast()请注意,使用 它只是降低了死锁发生的可能性,但它不会在所有情况下阻止死锁。wg尝试编译你的二进制文件,并在循环中运行它(例如从bash :),我想你会看到同样的问题有时会发生。-racefor i in {1..100}; do ./myprogram; done
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go