发布者/订阅者在循环期间更改订阅

时间:2011-05-26 13:58:27

标签: c++ loops publish-subscribe

这是我的一般设计查询。我通过维护订阅者列表实现了发布/订阅模式。当要发布的事件发生时,我遍历订阅者并依次将事件推送给每个订阅者。

我的问题发生在由于该出版物,软件深度的某个地方,所描述的组件决定取消订阅的另一个组件或事件。通过这样做,它们使我的迭代器失效并导致崩溃。

解决此问题的最佳方法是什么?我一直在考虑将整个发布循环包装到try catch块中,但这意味着一些订阅者会错过某人取消订阅的特定订阅,并且似乎有点过头了。然后我尝试将它喂回来,例如我将void发布调用转换为bool发布调用,当订阅者想要删除时返回true,这适用于该情况,但如果另一个订阅者取消订阅则不会。然后我想在某处“缓存”取消订阅请求并在循环完成时释放它们,但这似乎有点矫枉过正。然后我想将迭代器存储为类成员,这样我就可以从外部操作迭代器,但是这会变得混乱(比如你取消订阅订阅者1,迭代器指向2,容器是一个向量 - 然后是迭代器必须减少)。我想我可能更喜欢后两种解决方案中的一种,但两者似乎都不理想。

这是一个常见问题吗?有更优雅的解决方案吗?

1 个答案:

答案 0 :(得分:1)

您可以在发布期间禁止订阅操作,也可以使用适当的数据结构来保存订阅列表,或者两者兼而有之。

假设您将订阅者保留在std::list,那么您可以运行循环:

for(iterator_type it = subs.begin(); it != subs.end(); ) {
    iterator_type next = it;
    ++next;
    it->notifier();
    it = next;
}

这样,如果删除了当前项,则next中仍然有一个有效的迭代器。当然,在发布期间,您仍然不能允许任意删除(如果删除了next?)。

要允许任意删除,请将项目标记为无效并将其删除列表,直到可以安全删除:

... 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);