从地图擦除时崩溃



由于意外行为,我发生了崩溃。。我想问一下,我该如何修改代码来防御这次崩溃。

void SubscManag::handleNotif(Notif notif)
{

std::cout << "Current subscribe size: " << subs_.size();
std::map<int, SubscData>::iterator it;
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
it = subs_.begin();
}
for (;;)
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
if (it == subs_.end())
{
break;
}
if (it->second.hasToBeErased)
{
std::cout << "Erase id : " << it->first;
it = subs_.erase(it);
continue;
}
if (cond)
{
// ....
++it;
continue;
}
// ....
++it;
}
}

崩溃发生在it=subs_.erase(it(行;用这个

Erase id : 2
Erase id : 3
Erase id : 28473456

所以Element with id : 28473456可能不存在或已损坏,但我如何防御崩溃?非常感谢。

您正在尝试执行基于锁的并发。

基于锁的并发性不构成。本地正确的操作在连接时会生成垃圾。

这里的问题是,你的it是在一个锁中生成的,然后你就解锁了。在该解锁期间,it可能有效,也可能无效。然后重新定位并假定it仍然有效。

你要么必须锁定你在地图上工作的整个时期,然后解锁它,忘记锁中几乎所有你知道的东西(不带迭代器(,要么基于不可变的数据结构重写你的代码,或者做一些更激进的事情。

{
std::lock_guard<std::recursive_mutex> lock(mutex_);
it = subs_.begin();
}

你在这里锁定,做手术,然后。。。在作用域的末尾解锁互斥对象。

所以it是映射的迭代器,当你持有映射时,你不会阻止其他线程编辑它。这可能会使it无效。

同样的事情发生在for(;;)循环中;在每次迭代开始时锁定,然后在迭代结束时解锁。it引用的map元素可能会在一次迭代和下一次迭代之间被删除,从而使it无效。

这里有一个简单的实用程序类:

template<class T>
struct mutex_guarded {
template<class F>
auto read(F&& f)const {
auto l = std::unique_lock<std::mutex>(m);
return f(t);
}
template<class F>
auto write(F&& f) {
auto l = std::unique_lock<std::mutex>(m);
return f(t);
}
private:
mutable std::mutex m;
T t;
};

现在,将您的std::map<int, SubscData>更改为mutex_guarded<std::map<int, SubscData>>

下一篇:

void SubscManag::handleNotif(Notif notif)
{
subs_.write([&](auto& subs_) {
std::cout << "Current subscribe size: " << subs_.size();
auto it = subs_.begin();
for (;;)
{
if (it == subs_.end())
{
break;
}
if (it->second.hasToBeErased)
{
std::cout << "Erase id : " << it->first;
it = subs_.erase(it);
continue;
}
if (cond)
{
// ....
++it;
continue;
}
// ....
++it;
}
});
}

并且在访问subs_时会锁定互斥对象。

请注意,通过其他方法访问subs_仍然是危险的。用互斥锁保护单个方法调用是一个糟糕的计划。制作";这个对象可以从任何线程"访问;在999/1000的情况下,它会导致灾难,通常会延迟,最初的小情况会起作用,然后在编写代码很久之后,它就会在死锁和竞争条件下爆炸。

mutex_guarded不是最好的解决方案,但它比按方法锁定要好。

相关内容

  • 没有找到相关文章

最新更新