登录
首页 >  Golang >  Go问答

Go函数中的go func为何需要waitgroup以确保正确退出?

来源:stackoverflow

时间:2024-02-15 13:54:22 125浏览 收藏

大家好,今天本人给大家带来文章《Go函数中的go func为何需要waitgroup以确保正确退出?》,文中内容主要涉及到,如果你对Golang方面的知识点感兴趣,那就请各位朋友继续看下去吧~希望能真正帮到你们,谢谢!

问题内容

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

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

当我注释一些行并再次运行它时,它会抛出致命错误 “所有 goroutine 都在睡觉 - 死锁!”

更改后的订阅功能如下所示:

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

让我困惑的是go func是否在外部subscribe函数返回之前执行。在我看来,尽管外部函数已返回,但 go func 将作为守护进程运行,因此 wg 变量是不必要的。但这表明我完全错了。那么如果go func有没有被调度的可能性,是否意味着我们必须在每个函数或代码块中使用sync.waitgroup来确保该goroutine在函数或代码块返回之前被调度执行? p>

谢谢大家。


正确答案


问题在于,任一调用中的 c.wait() 都不能保证在 button.clicked.broadcast() 之前运行;即使您的原始代码使用 waitgroup 也不能保证它(因为它是 c.wait() 部分,而不是重要的 goroutine 的生成)

修改订阅:

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

这是基于这样的观察:[2] 只能在执行 [2] 的所有先前 goroutine 开始时或之后发生,由于它们共享锁,所以它们都保留在 c.wait 上。所以subwg.wait(),意思是2(或者订阅数)[2]被执行,只有可能有一个goroutine没有持有c.wait,可以通过询问locker来解决再次到 lock

演示:https://play.golang.org/p/6mjUEcn3ec5

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

因此,当您的主函数到达 button.clicked.broadcast() 时,两个 goroutine 很可能实际上正在等待它们的 button.clicked.wait() 调用。

如果没有 wg,你就无法保证 goroutine 是否已经启动,并且你的代码可能会过早调用 button.clicked.broadcast()

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

尝试使用 -race 编译二进制文件,然后循环运行它(例如从 bash : for i in {1..100}; do ./myprogram; dide),我想您会看到同样的问题有时会发生。

好了,本文到此结束,带大家了解了《Go函数中的go func为何需要waitgroup以确保正确退出?》,希望本文对你有所帮助!关注golang学习网公众号,给大家分享更多Golang知识!

声明:本文转载于:stackoverflow 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>