同步。电导率在多个 goroutine 中无法正常唤醒

sync.Cond not woking properly with multiple goroutines

提问人:cool_fire 提问时间:10/22/2023 更新时间:10/22/2023 访问量:51

问:

我尝试仅在广播时打印多个 go 例程,但 go 例程死锁。有没有办法改进下面的代码,有什么方法可以确定为什么这个代码是死锁的?sync.Cond

package main

import (
    "fmt"
    "sync"
)

type Button struct {
    Clicked *sync.Cond
}

func subscribe(cond *sync.Cond, btnMessage string, wg *sync.WaitGroup) {
    defer wg.Done()
    cond.L.Lock()
    defer cond.L.Unlock()

    cond.Wait()
    fmt.Println(btnMessage)
}

func test6() {
    var wg sync.WaitGroup

    button := Button{
        Clicked: sync.NewCond(&sync.Mutex{}),
    }

    wg.Add(3)
    go subscribe(button.Clicked, "Button 1", &wg)
    go subscribe(button.Clicked, "Button 2", &wg)
    go subscribe(button.Clicked, "Button 3", &wg)

    // Start the goroutines before broadcasting
    button.Clicked.Broadcast()

    wg.Wait()
}

func main() {
    test6()
}

我在下面遇到了死锁错误

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x466df7?)
    /nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/runtime/sema.go:62 +0x25
sync.(*WaitGroup).Wait(0x405399?)
    /nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/sync/waitgroup.go:139 +0x52
main.test6()
    /home/runner/concurrency-in-go/main.go:36 +0x199
main.main()
    /home/runner/concurrency-in-go/main.go:40 +0x17

goroutine 6 [sync.Cond.Wait]:
sync.runtime_notifyListWait(0xc000108050, 0x1)
    /nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/runtime/sema.go:517 +0x14c
sync.(*Cond).Wait(0x0?)
    /nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/sync/cond.go:70 +0x8c
main.subscribe(0xc000108040, {0x49962f, 0x8}, 0x0?)
    /home/runner/concurrency-in-go/main.go:17 +0xbb
created by main.test6
    /home/runner/concurrency-in-go/main.go:29 +0xdb

goroutine 7 [sync.Cond.Wait]:
sync.runtime_notifyListWait(0xc000108050, 0x2)
    /nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/runtime/sema.go:517 +0x14c
sync.(*Cond).Wait(0x0?)
    /nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/sync/cond.go:70 +0x8c
main.subscribe(0xc000108040, {0x499637, 0x8}, 0x0?)
    /home/runner/concurrency-in-go/main.go:17 +0xbb
created by main.test6
    /home/runner/concurrency-in-go/main.go:30 +0x12f

goroutine 8 [sync.Cond.Wait]:
sync.runtime_notifyListWait(0xc000108050, 0x0)
    /nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/runtime/sema.go:517 +0x14c
sync.(*Cond).Wait(0x0?)
    /nix/store/akhjsmrrsakcnj8x3xgygvizhccbyn0v-go-1.19.3/share/go/src/sync/cond.go:70 +0x8c
main.subscribe(0xc000108040, {0x49963f, 0x8}, 0x0?)
    /home/runner/concurrency-in-go/main.go:17 +0xbb
created by main.test6
    /home/runner/concurrency-in-go/main.go:31 +0x185
exit status 2
多线程 go 并发 同步 goroutine

评论


答:

3赞 Eli Davis 10/22/2023 #1

你甚至在你的 goroutines 初始化之前就已经广播了。所以你最终会广播,然后再次等待。如果你在主线程中插入一个睡眠,你可以看到它开始工作。

游乐场与它一起工作

只有当满足某些条件时,您才应该在 goroutine 中等待。根据您的实际用例,您可以考虑使用不同的同步原语。

评论

1赞 Kaveh Shahbazian 10/23/2023
为了证明这一点,如果我们等待 goroutines 启动,然后广播,它就会起作用。go.dev/play/p/VP9f_LvIBXB
0赞 cool_fire 10/23/2023
感谢您的解释,我明白为什么它不起作用,感谢您的努力