所有的Go协程在使用sync.Cond时都处于休眠状态。

huangapple go评论118阅读模式
英文:

all go routines are asleep while using sync.Cond

问题

  1. func main() {
  2. type Button struct {
  3. Clicked *sync.Cond
  4. }
  5. button := Button{Clicked: sync.NewCond(&sync.Mutex{})}
  6. subscribe := func(c *sync.Cond, fn func()) {
  7. //var goroutineRunning sync.WaitGroup
  8. //goroutineRunning.Add(1)
  9. go func() {
  10. //goroutineRunning.Done()
  11. c.L.Lock()
  12. defer c.L.Unlock()
  13. c.Wait()
  14. fn()
  15. }()
  16. //goroutineRunning.Wait()
  17. }
  18. var clickRegistered sync.WaitGroup
  19. clickRegistered.Add(3)
  20. subscribe(button.Clicked, func() {
  21. fmt.Println("Maximizing window")
  22. clickRegistered.Done()
  23. })
  24. subscribe(button.Clicked, func() {
  25. fmt.Println("Displaying annoying dialogue box!")
  26. clickRegistered.Done()
  27. })
  28. subscribe(button.Clicked, func() {
  29. fmt.Println("Mouse clicked.")
  30. clickRegistered.Done()
  31. })
  32. button.Clicked.Broadcast()
  33. clickRegistered.Wait()
  34. }

我正在遵循《Go并发编程》一书中的一些示例,并且我已经在subscribe方法中注释了一些代码语句。有人可以帮忙解释一下为什么这段代码会发生死锁吗?

英文:
  1. func main() {
  2. type Button struct {
  3. Clicked *sync.Cond
  4. }
  5. button := Button{Clicked: sync.NewCond(&sync.Mutex{})}
  6. subscribe := func(c *sync.Cond, fn func()) {
  7. //var goroutineRunning sync.WaitGroup
  8. //goroutineRunning.Add(1)
  9. go func() {
  10. //goroutineRunning.Done()
  11. c.L.Lock()
  12. defer c.L.Unlock()
  13. c.Wait()
  14. fn()
  15. }()
  16. //goroutineRunning.Wait()
  17. }
  18. var clickRegistered sync.WaitGroup
  19. clickRegistered.Add(3)
  20. subscribe(button.Clicked, func() {
  21. fmt.Println("Maximizing window")
  22. clickRegistered.Done()
  23. })
  24. subscribe(button.Clicked, func() {
  25. fmt.Println("Displaying annoying dialogue box!")
  26. clickRegistered.Done()
  27. })
  28. subscribe(button.Clicked, func() {
  29. fmt.Println("Mouse clicked.")
  30. clickRegistered.Done()
  31. })
  32. button.Clicked.Broadcast()
  33. clickRegistered.Wait()
  34. }

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发出信号,告诉它们正在运行。但实际上,你也可以使用条件本身来实现这一点:

  1. subscribe := func(c *sync.Cond, fn func()) {
  2. c.L.Lock() // 同步地获取锁,一旦goroutine执行了`c.Wait()`,锁就会再次可用
  3. go func() {
  4. defer c.L.Unlock()
  5. c.Wait()
  6. fn()
  7. }()
  8. }
  9. subscribe(...)
  10. subscribe(...)
  11. subscribe(...)
  12. 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 :

  1. subscribe := func(c *sync.Cond, fn func()) {
  2. c.L.Lock() // take the lock synchronously, the lock will be available
  3. // again once the goroutine executes 'c.Wait()'
  4. go func() {
  5. defer c.L.Unlock()
  6. c.Wait()
  7. fn()
  8. }()
  9. }
  10. subscribe(...)
  11. subscribe(...)
  12. subscribe(...)
  13. 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.

huangapple
  • 本文由 发表于 2021年10月17日 23:01:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/69605585.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定