当删除其中一个迭代对象时,任务会导致段错误

时间:2013-10-30 19:06:40

标签: c++ iterator openmp

下一个代码崩溃,因为其中一个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
}

2 个答案:

答案 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!
    }
}