Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 为什么go函数中的go func需要waitgroup才能正确退出?_Multithreading_Go_Synchronization_Race Condition_Goroutine - Fatal编程技术网

Multithreading 为什么go函数中的go func需要waitgroup才能正确退出?

Multithreading 为什么go函数中的go func需要waitgroup才能正确退出?,multithreading,go,synchronization,race-condition,goroutine,Multithreading,Go,Synchronization,Race Condition,Goroutine,Sry这个标题可能有误导性。实际上,完整代码如下所示: package main import ( "fmt" "sync" ) type Button struct { Clicked *sync.Cond } func main() { button := Button{ Clicked: sync.NewCond(&sync.Mutex{}), } subscribe :=

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()
}
当我注释一些行并再次运行它时,它会抛出一个致命错误“所有goroutines都处于休眠状态-死锁!”
修改后的订阅功能如下所示:

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

谢谢大家。

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

因此,当您的主函数到达
按钮.Clicked.Broadcast()
时,两个goroutine很有可能正在等待它们的
按钮.Clicked.Wait()
调用

如果没有
wg
,您无法保证goroutines已经启动,您的代码可能会过早地调用
按钮.Clicked.Broadcast()


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


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

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

因此,当您的主函数到达
按钮.Clicked.Broadcast()
时,两个goroutine很有可能正在等待它们的
按钮.Clicked.Wait()
调用

如果没有
wg
,您无法保证goroutines已经启动,您的代码可能会过早地调用
按钮.Clicked.Broadcast()


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


尝试使用
-race
编译二进制文件,并在循环中运行它(例如,从bash开始:
for i in{1..100};do./myprogram;done
),我想您会看到有时也会出现同样的问题。

问题是,在
按钮之前,两个调用中的
c.Wait()
都不能保证运行;甚至您的原始代码使用
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
,这可以通过另一次请求锁柜锁定
来解决


游乐场:

问题是,在
按钮之前,两个调用中的
c.Wait()
都不能保证运行;甚至您的原始代码使用
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
,这可以通过另一次请求锁柜锁定
来解决


操场:

谢谢@LeGEC我明白你的意思了。但是为什么我使用的
wg
仍然存在问题?您的意思是当主goroutine到达
按钮。单击.Broadcast()
调用时,两个goroutine可能没有到达
c.Wait()
?i、 e.
广播
在1或2个goroutines等待
单击的sync.Cond
之前被调用。我说得对吗?谢谢你的耐心。@shengjiang:是的,没错。看起来您正在编写一些测试代码,所以我不会尝试构建太复杂的东西:一个简单的解决方法(它仍然不完整,但在实践中会很好地工作),就是在调用
.Broadcast()
之前等待一段时间,非常感谢!我正在学习多线程编程。我在
c.L.Lock()
下面添加了
time.Sleep(1*time.Second)
,死锁按预期再次发生。再次感谢你的帮助@盛江:检查@leadbebebop的答案,他正确地建议在主函数中获取
cond.L.Lock()
,以确保其他goroutine是
。Wait()
在该条件下运行谢谢@LeGEC我明白了你的意思。但是为什么我使用的
wg
仍然存在问题?您的意思是当主goroutine到达
按钮。单击.Broadcast()
调用时,两个goroutine可能没有到达
c.Wait()
?i、 e.在之前调用
广播