func main() {
type Button struct {
Clicked *sync.Cond
}
button := Button{Clicked: sync.NewCond(&sync.Mutex{})}
subscribe := func(c *sync.Cond, fn func()) {
//var goroutineRunning sync.WaitGroup
//goroutineRunning.Add(1)
go func() {
//goroutineRunning.Done()
c.L.Lock()
defer c.L.Unlock()
c.Wait()
fn()
}()
//goroutineRunning.Wait()
}
var clickRegistered sync.WaitGroup
clickRegistered.Add(3)
subscribe(button.Clicked, func() {
fmt.Println("Maximizing window")
clickRegistered.Done()
})
subscribe(button.Clicked, func() {
fmt.Println("Displaying annoying dialogue box!")
clickRegistered.Done()
})
subscribe(button.Clicked, func() {
fmt.Println("Mouse clicked.")
clickRegistered.Done()
})
button.Clicked.Broadcast()
clickRegistered.Wait()
}
我正在关注 concurrency in go
一书中的一些示例,并且我在 subscribe 方法中注释了一些代码语句。有人可以帮忙解释为什么这段代码会死锁吗。
最佳答案
注意:就命名而言,clickRegistered
不符合您代码的当前行为。 handlerExecuted
会更准确。
解决死锁的一种方法:
从您的代码开始:https://play.golang.org/p/XEduyON9j59
您可以将一个额外的 waitGroup 或一个 channel 传递给您的 goroutine,以便它们可以向主 goroutine 发出它们正在运行的信号,但实际上您也可以使用条件本身来做到这一点:
subscribe := func(c *sync.Cond, fn func()) {
c.L.Lock() // take the lock synchronously, the lock will be available
// again once the goroutine executes 'c.Wait()'
go func() {
defer c.L.Unlock()
c.Wait()
fn()
}()
}
subscribe(...)
subscribe(...)
subscribe(...)
button.Clicked.L.Lock() // will return only once all subscribers are '.Wait()'ing
https://play.golang.org/p/fFzRolUnaDZ
这是解决死锁的另一种方法,使用 Waitgroup
让 goroutines 发出它们正在运行的信号:
https://play.golang.org/p/LSM72HBmo0M
订阅的运行方式可能感觉更“并发”;但是,您会注意到,要知道它们实际上正在运行它们的 .Wait()
指令,唯一直接的方法仍然是获取条件锁。
关于go - 使用 sync.Cond 时所有 go routines 都处于休眠状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69605585/