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

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

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.

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:

确定