Golang测试goroutine竞态导致发布消息早于订阅监听问题求助
竞态问题根源定位
核心问题1:Topic结构体方法错误使用值接收者,引发sync.Map拷贝异常
- 你当前定义的
Activate、Dispatch方法均为值接收者(t Topic),调用时会对整个Topic结构体做值拷贝 - Topic结构体中
sync.Map类型的Subscribers字段官方明确禁止拷贝,拷贝后会出现内部状态不一致:监听协程操作的是拷贝后的Subscribers实例,发布逻辑遍历的是原结构体的Subscribers实例,双方无法看到彼此的修改,自然无法找到新增的订阅者。 - 修复方案:将所有Topic关联方法的接收者统一修改为指针类型
(t *Topic)。
核心问题2:订阅完成信号的触发时机过早
- 你当前测试中使用的
subscribed通道,是在useCase.Subscribe方法返回后立即关闭,但是Subscribe方法仅完成了将订阅者发送到newSubCh通道的操作,并没有等待listenForSubscriptions协程将订阅者实际写入SubscribersMap - 主线程收到
subscribed关闭信号后立即调用Publish发布消息,极大概率出现消息处理逻辑先于订阅写入逻辑执行的情况,遍历Subscribers时还未写入新增订阅者,直接导致消息丢失,测试断言失败。 - 修复方案:在订阅逻辑中增加同步机制,确保订阅者被实际写入
SubscribersMap后再返回,或者在测试中增加额外的同步逻辑等待订阅写入完成。
核心问题3:监听协程的启动调度无同步保证
- 调用
Activate方法启动三个监听协程后,协程什么时候被Go调度器执行是完全不确定的,如果调用Subscribe往newSubCh发送订阅者时,listenForSubscriptions协程还未启动监听,会导致addSubscriber操作阻塞,若newSubCh为无缓冲通道甚至会直接卡死测试。 - 修复方案:在
Activate方法中引入sync.WaitGroup,等待三个监听协程都进入for range监听逻辑后再返回,确保通道监听已经就绪后再执行后续订阅、发布操作。
内容的提问来源于stack exchange,提问作者Ming
相关产品推荐
相关产品推荐

