Go语言Future模式实现中,为何需将WaitGroup.Wait放入goroutine?
为什么
wg.Wait()必须放在goroutine里? 问题的核心在于无缓冲通道的阻塞特性和代码执行顺序的冲突,我们分两种情况拆解:
情况1:同步执行wg.Wait()(移除goroutine)
当你把wg.Wait()放在返回的函数里同步执行时,代码执行流程会彻底卡住:
- main里调用
future(),进入返回的匿名函数; - 立刻执行
wg.Wait(),等待所有checkUrl的goroutine完成; - 但此时
checkUrl的goroutine们正在执行c <- urlStatus{...}——由于c是无缓冲通道,发送操作会阻塞直到有接收方读取数据; - 而consumer的goroutine还没开始接收(因为
future()还没返回通道c,consumer根本拿不到通道); - 最终形成死锁:
checkUrlgoroutine阻塞在发送数据,wg.Wait()阻塞在等待这些goroutine结束,main goroutine阻塞在future()的调用上,整个程序彻底停住。
情况2:把wg.Wait()放在goroutine里(正确写法)
这种写法的执行流程是完整闭环的:
- main调用
future(),进入返回的匿名函数; - 启动一个新goroutine去执行
wg.Wait()和关闭通道,当前函数立刻返回通道c; - consumer的goroutine拿到通道
c,开始通过range接收数据; checkUrl的goroutine发送数据时,因为有consumer在接收,不会阻塞,完成后调用wg.Done();- 所有
checkUrl完成后,wg.Wait()返回,关闭通道c; - consumer遍历完通道里的所有数据后,发送
done信号,main收到后结束程序。
本质原因
无缓冲通道的发送/接收是同步阻塞的,必须同时有发送方和接收方才能完成操作。如果先等待所有发送方完成再给接收方通道,发送方会因为没有接收方而永久阻塞,进而导致等待操作也永久阻塞。把wg.Wait()放到goroutine里,就能让通道先被接收方拿到,保证发送操作能正常完成,最终整个流程顺畅闭环。
内容的提问来源于stack exchange,提问作者Coronavirus
相关产品推荐
相关产品推荐

