下一个代码崩溃,因为其中一个OpenMP任务删除了it
指向的一个元素。我怎么能解决这个问题?代码必须使用OpenMP任务来实现。
#pragma omp parallel
{
#pragma omp single nowait
{
for (std::list<Class*>::iterator it = myClass.begin(); it != myClass.end();) {
if ((*it)->getNumber() == 0) {
#pragma omp critical
it = myClass.erase(it);
}
else {
#pragma omp task firstprivate(it)
{
bool result = (*it)->function(t);
if ( result ) {
#pragma omp critical
it = myClass.erase(it);
}
}
}
++it;
}
}
#pragma omp taskwait
}
答案 0 :(得分:1)
使用多个线程从std::list
擦除节点非常棘手。您不仅要删除关键区域中的节点,还要处理用于列表遍历的迭代器。
当您使用一个线程执行++it
和其他线程来执行list.erase(it)
时,您可能会发现it
指向的节点可能已在++it
之前删除已完成,并且在指向不存在的节点的迭代器上执行++
将导致未定义的行为。
一种可能的解决方案是,您确保在删除/更改节点之前完成++it
,并且如@sehe所示,您不应在++it
之后erase
。
#pragma omp parallel
{
#pragma omp single nowait
{
for (std::list<Class*>::iterator it = myClass.begin(); it != myClass.end();) {
if ((*it)->getNumber() == 0) {
#pragma omp critical
it = myClass.erase(it);
}
else {
std::list<Class*>::iterator local_it=it++;
#pragma omp task firstprivate(local_it)
{
bool result = (*local_it)->function(t);
if ( result ) {
#pragma omp critical
myClass.erase(local_it);
}
}
}
}
}
#pragma omp taskwait
}
答案 1 :(得分:0)
你不应该在it++
之后递增erase
,因为你可能会跑过末尾迭代器(如果你删除了最后一个元素)。
一个简单的演示(观察assert
成功的方式!):
#include <list>
#include <cassert>
struct Class { int getNumber() const { return 0; } };
int main()
{
std::list<Class*> l { new Class() };
for(auto it = l.begin(); it != l.end();)
{
if((*it)->getNumber() == 0)
it = l.erase(it);
assert(it == l.end());
it++; // OOOOOPS!
}
}