dan*_*jar 0 c++ multithreading iterator vector c++11
我的应用程序中有一个全局事件管理器.每个组件都可以监听事件并触发它们.我们来看看窗口组件.它拥有一个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.
// ...
}
}
};
Run Code Online (Sandbox Code Playgroud)
问题是当"keydown"事件被触发时,无论是从更新循环内部还是从另一个线程触发,代码都会崩溃.我想这是因为在擦除元素后迭代器不再有效.在不知道何时发生擦除的情况下,如何安全地迭代变化的向量?
该错误表示i无法取消引用.我尝试通过try catch块包装循环体,但是没有异常可以捕获,只是来自Visual Studio的调试断言.
在不知道何时发生擦除的情况下,如何安全地迭代变化的向量?
你不能.
std::vector在被另一个线程修改时访问(或任何其他标准容器)是未定义的行为.
您需要确保修改向量的线程阻止任何其他线程同时访问它,例如使用互斥锁.
在C++ 14中,您可以使用a 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())
// ...
}
}
};
Run Code Online (Sandbox Code Playgroud)
(注意,我改为i.is_open(),i->is_open()因为我认为这是你在代码中真正拥有的东西.)
这只有在更改由另一个线程完成时才有用,如果更新循环触发了keydown事件,它将会死锁.
另一个解决方案是推迟从向量中删除项目并定期清理"死"项.在修改向量时,仍然需要一个互斥锁来防止并发访问,但只能通过锁定一小段代码来避免死锁.就像是:
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();
}
};
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
932 次 |
| 最近记录: |