解析subscribe函数中goroutineRunning WaitGroup的存在原因与必要性
先拆解这段代码的核心逻辑:subscribe用来注册按钮点击监听——每调用一次,就启动一个goroutine,让它阻塞在sync.Cond的Wait()方法上,等主线程调用Broadcast()时,再执行对应的回调函数。
你觉得移除这个WaitGroup代码还能正常运行?大概率某次测试确实能输出结果,但这完全是依赖goroutine调度的运气,本质上藏着严重的死锁bug:
如果删掉goroutineRunning,subscribe函数启动goroutine后会立刻返回。主线程会快速完成三次subscribe调用,紧接着执行Broadcast()。这时候,某个刚启动的goroutine可能还没来得及执行到c.Wait()——也就是说它还没加入到sync.Cond的等待队列里,Broadcast()发出的唤醒信号就直接被错过了。
一旦信号没接住,这个goroutine会永远卡在Wait()上,对应的clickRegistered.Done()也不会执行。主线程最后会卡在clickRegistered.Wait()处,程序直接死锁,根本无法正常结束。
那这个WaitGroup的核心作用是什么?它强制subscribe函数必须等新启动的goroutine已经开始运行(执行了goroutineRunning.Done())之后才返回。这样,当主线程走完三次subscribe、准备调用Broadcast()的时候,所有三个监听goroutine都已经在往Wait()的步骤推进了——要么已经进入等待状态,要么在排队抢锁准备进入等待。这时候再触发Broadcast(),所有goroutine都能接收到信号,回调函数都会执行,程序才能正常收尾。
简单总结:它是用来保证所有监听goroutine都做好了接收唤醒信号的准备,避免因goroutine调度延迟导致信号丢失,是防止程序死锁的关键。
内容的提问来源于stack exchange,提问作者Hamza Masood

