英文:
all go routines are asleep while using sync.Cond
问题
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()
}
我正在遵循《Go并发编程》一书中的一些示例,并且我已经在subscribe方法中注释了一些代码语句。有人可以帮忙解释一下为什么这段代码会发生死锁吗?
英文:
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()
}
I am following some examples from concurrency in go
book, and I have commented some code statements inside subscribe method. can someone please help in explaining why this code deadlocks.
答案1
得分: 2
一个注意事项:就命名而言,clickRegistered
不符合你的代码当前行为。handlerExecuted
可能更准确一些。
修复死锁的一种方法:
从你的代码开始:https://play.golang.org/p/XEduyON9j59
你可以传递一个额外的waitGroup
或通道给你的goroutine,这样它们就可以向主goroutine发出信号,告诉它们正在运行。但实际上,你也可以使用条件本身来实现这一点:
subscribe := func(c *sync.Cond, fn func()) {
c.L.Lock() // 同步地获取锁,一旦goroutine执行了`c.Wait()`,锁就会再次可用
go func() {
defer c.L.Unlock()
c.Wait()
fn()
}()
}
subscribe(...)
subscribe(...)
subscribe(...)
button.Clicked.L.Lock() // 只有当所有订阅者都在`.Wait()`时才会返回
https://play.golang.org/p/fFzRolUnaDZ
这是另一种修复死锁的方法,使用WaitGroup
让goroutine发出它们正在运行的信号:
https://play.golang.org/p/LSM72HBmo0M
订阅的运行方式可能更加"并发",但你会注意到,要知道它们是否真正运行了.Wait()
指令的唯一直接方法仍然是获取条件的锁。
英文:
a note : as far as naming goes, clickRegistered
doesn't fit the current behavior of your code. handlerExecuted
would be more accurate.
One way to fix the deadlock :
starting from your code : https://play.golang.org/p/XEduyON9j59
You may pass an extra waitGroup or a channel to your goroutines, so that they can signal the main goroutine that they are running, but actually you can also use the condition itself to do that :
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
Here is another way to fix the deadlock, using a Waitgroup
to let the goroutines signal that they are running:
https://play.golang.org/p/LSM72HBmo0M
The way the subscriptions are run may feel more "concurrent"; you will note, however, that the only straightforward way to know that they are actually running their .Wait()
instruction is still to acquire the condition's lock.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论