1

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

2 回答 2

1

使用多个线程擦除节点std::list非常棘手。您不仅要擦除临界区中的节点,还要注意用于列表遍历的迭代器。

当您使用一个线程做++it而其他线程做时list.erase(it),您可能会发现所指向的节点在完成it之前可能已经被擦除++it,并且在++指向不存在的节点的迭代器上执行将导致未定义的行为。

一种可能的解决方案是您确保++it在删除/更改节点之前完成,并且如@sehe 所示,您不应该++iterase.

#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
}
于 2013-10-31T11:42:43.883 回答
0

您根本不应该it++在 after 之后递增erase,因为您可能会跑过 end 迭代器(如果您删除了最后一个元素)。

一个简单的演示(看看如何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!
    }
}
于 2013-10-31T09:52:55.947 回答