C++ 安全地在std::vector上迭代,同时可以删除项
我的应用程序中有一个全局事件管理器。每个组件都可以侦听事件并触发它们。让我们来看看窗口组件。它持有一个windows的C++ 安全地在std::vector上迭代,同时可以删除项,c++,multithreading,c++11,vector,iterator,C++,Multithreading,C++11,Vector,Iterator,我的应用程序中有一个全局事件管理器。每个组件都可以侦听事件并触发它们。让我们来看看窗口组件。它持有一个windows的std::vector,它定期对其进行迭代以处理输入等。此外,当按下退出键时,它会注册到“keydown”事件以删除活动窗口 class window : public module { window() { // Listen to key event listen("keydown", [=](size_t index, int cod
std::vector
,它定期对其进行迭代以处理输入等。此外,当按下退出键时,它会注册到“keydown”
事件以删除活动窗口
class window : public module {
window() {
// Listen to key event
listen("keydown", [=](size_t index, int code) {
if (code == 27) {
windows[index].close();
windows.erase(windows.begin() + index);
}
});
}
void update() {
for (auto i = windows.begin(); i != windows.end(); i++) {
if (i->is_open()) // On remove, crashes at this line.
// ...
}
}
};
问题在于,当从更新循环内部或从另一个线程触发“keydown”
事件时,代码崩溃。我猜这是因为迭代器在删除元素后不再有效。在不知道擦除何时发生的情况下,如何安全地迭代不断变化的向量
错误表明,i
无法取消引用。我尝试用try-catch块包装循环体,但是catch没有异常,只是来自visualstudio的一个调试断言
在不知道擦除何时发生的情况下,如何安全地迭代不断变化的向量
你不能
在另一个线程修改时访问std::vector
(或任何其他标准容器)是未定义的行为
您需要确保修改向量的线程阻止任何其他线程并发访问向量,例如使用互斥锁
在C++14中,您可以使用std::shared_timed_mutex
来保护向量,这样只需要读取向量的线程就可以使用共享锁,但是想要修改向量的线程在进行更改时可以使用排他锁
class window : public module {
std::shared_timed_mutex m_mutex;
window() {
// Listen to key event
listen("keydown", [=](size_t index, int code) {
if (code == 27) {
std::unique_lock<std::shared_timed_mutex> lock(m_mutex);
windows[index].close();
windows.erase(windows.begin() + index);
}
});
}
void update() {
std::shared_lock<std::shared_timed_mutex> lock(m_mutex);
for (auto i = windows.begin(); i != windows.end(); i++) {
if (i->is_open())
// ...
}
}
};
如果向量被重新定位,您希望
i
、windows.begin()
或windows.end()
中的任何一个指向什么?“我认为你需要重新考虑你的设计。”本杰明·巴尼耶说,“所以我需要一个间接的层次。”。你知道吗?如果更新循环触发keydown事件呢?我认为这会导致死锁。有什么东西可以充当递归共享定时互斥锁吗?@danijar。是的,它会死锁。如果发生这种情况,你应该考虑把元素放在向量中,但可能把它添加到要删除的元素列表中,然后在没有其他访问向量时定期删除所有已过期的元素。@ RasalmielNeZuk,不。递归互斥通常是围绕一个坏的设计而不是一个解决方案工作的黑客。在我的真实代码中,不仅事件,而且向量都是全局管理的。管理器提供开始和结束迭代器以及用于插入、随机访问和删除的函数。因此,添加互斥锁并将要删除的项附加到列表中是很容易的。但是,由于manager没有定期更新,它如何知道何时应用删除?
class window : public module {
std::mutex m_mutex;
std::vector<size_t> m_expired;
window() {
// Listen to key event
listen("keydown", [=](size_t index, int code) {
if (code == 27) {
windows[index].close();
std::lock_guard<std::mutex> lock(m_mutex);
m_expired.push_back(index);
}
});
}
void update() {
erase_expired();
for (auto i = windows.begin(); i != windows.end(); i++) {
if (i->is_open())
// ...
}
}
void erase_expired()
{
std::lock_guard<std::mutex> lock(m_mutex);
std::sort(m_expired.begin(), m_expired.end(), std::greater<>{});
for (auto idx : m_expired)
windows.erase(windows.begin() + idx);
m_expired.clear();
}
};