【发布时间】:2022-07-06 08:14:23
【问题描述】:
https://go.dev/play/p/YVYRWSgcp4u
我在“Go 开发者的并发工具和技术”中看到了这段代码,其中提到了广播的用法,上下文是使用广播来唤醒三个 gorouting。
package main
import (
"fmt"
"sync"
"time"
)
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() // <---- why here?
//fmt.Println("wg already done")
c.L.Lock()
defer c.L.Unlock()
c.Wait()
fn()
//goroutineRunning.Done(), if put here will result in deadlock, why?
}()
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 dialog box!")
clickRegistered.Done()
})
subscribe(button.Clicked, func() {
fmt.Println("Mouse clicked.")
clickRegistered.Done()
})
time.Sleep(time.Second * 3)
button.Clicked.Broadcast()
clickRegistered.Wait()
}
我正在尝试了解订阅部分
subscribe := func(c *sync.Cond, fn func()) {
var goroutineRunning sync.WaitGroup
goroutineRunning.Add(1)
go func() {
goroutineRunning.Done()
//fmt.Println("wg already done")
c.L.Lock()
defer c.L.Unlock()
c.Wait()
fn()
//goroutineRunning.Done()
//fmt.Println("fn executed")
}()
goroutineRunning.Wait()
}
作者说:
这里我们定义了一个方便函数,它允许我们注册函数来处理来自条件的信号。每个处理程序都在自己的 goroutine 上运行,并且在确认该 goroutine 正在运行之前订阅不会退出。
我的理解是我们应该在gorouting里面defer goroutingRunning.Done()这样后面的代码(包括等待Cond和fn()调用,才有机会
运行),但在这种情况下,goroutingRunning.Done() 似乎必须在 gorouting 的开头,否则会导致死锁错误,但为什么呢?
-----更新------
我们实际上可以通过这种方式摆脱订阅功能中的等待组:
subscribe := func(c *sync.Cond, fn func(), wg *sync.WaitGroup) {
c.L.Lock()
defer c.L.Unlock()
c.Wait()
fn()
wg.Done()
}
var ClickRegistered sync.WaitGroup
ClickRegistered.Add(3)
go subscribe(button.Clicked, func() {
fmt.Println("do 1")
}, &ClickRegistered)
go subscribe(button.Clicked, func() {
fmt.Println("do 2")
}, &ClickRegistered)
go subscribe(button.Clicked, func() {
fmt.Println("do 3")
}, &ClickRegistered)
time.Sleep(time.Millisecond * 50)
fmt.Println("some process in main go routine finished")
button.Clicked.Broadcast()
ClickRegistered.Wait()
【问题讨论】:
标签: go synchronization deadlock waitgroup