问题描述
|
这更多是我的一般设计查询。我通过维护订户列表实现了一种发布/订阅模式。当要发布的事件发生时,我会遍历订阅者,并将事件依次推送到每个订阅者。
当由于该出版物,软件深度的某个地方,另一个组件或所描述的组件决定退订自己的事件而发生我的问题。这样做会使我的迭代器无效,并导致崩溃。
解决此问题的最佳方法是什么?我一直在考虑将整个发布循环包装到try catch块中,但这意味着某些订阅者会错过某人取消订阅的特定订阅,这似乎有点过头了。然后我尝试将其反馈给我将void publish调用转换为bool publish调用,该调用在订阅者想要删除时返回true,这种情况适用于这种情况,但如果另一个订阅者取消订阅则不然。然后,我考虑在某个地方“高速缓存”取消订阅请求,并在循环完成后释放它们,但这似乎有点过头了。然后,我考虑将迭代器存储为类成员,以便可以从外部操作迭代器,但这会造成混乱(例如,您取消订阅者1的订阅,迭代器指向2,而容器是向量-那么迭代器将必须减少)。我认为我可能更喜欢后两种解决方案之一,但两者似乎都不理想。
这是个常见的问题吗?有没有更优雅的解决方案?
解决方法
您可以在发布期间禁止订阅操作,或者可以使用适当的数据结构来保存您的订阅列表,或同时使用两者。
假设您将订户保持在“ 0”,则可以这样运行循环:
for(iterator_type it = subs.begin(); it != subs.end(); ) {
iterator_type next = it;
++next;
it->notifier();
it = next;
}
这样,如果当前项被删除,则next
中仍然有一个有效的迭代器。当然,在发布过程中,您仍然不允许任意删除(如果删除了“ 2”,该怎么办?)。
要允许任意删除,请将一个项目标记为无效,然后将其列表删除推迟到安全为止:
... publication loop ...
dontRemoveItems = true;
for(iterator_type it = subs.begin(); it != subs.end(); ++it) {
if(it->valid)
it->notifier();
}
std::erase(std::remove_if(...,IsNotValid),...);
dontRemoveItems = false;
别处,
... removal code:
if(dontRemoveItems) item->valid = false;
else subs.erase(item);