Странная проблема concurrency с STL/OpenMP в 64-битных сборках

У меня есть странная проблема, когда я строю один из наших проектов в 64-битной конфигурации отладки. Похоже, что возникает какое-то странное поведение, которое немного похоже на то, что один итератор получает несколько раз. Я сузил его до следующего тестового кода:

#include <omp.h>

#define _HAS_ITERATOR_DEBUGGING 0

#include <vector>
#include <set>

int main(int argc, const char* argv[]) {    
   std::vector<int> v;
   for(int j = 0; j < 20; ++j) {
      v.push_back(j);
   }

   #pragma omp parallel for
   for(int i = 0; i < 100000; ++i) {
      std::set<int> s;
      std::vector<int>::const_iterator begin = v.begin(), end = v.end();
      s.insert(begin, end); // after this line s should contain the numbers 0-19
      if(s.size() != 20) {
         printf("fail\n");
         exit(3);
      }
   }
   return 0;
}

Тем не менее, тест размера часто терпит неудачу, что подразумевает, что каким-то образом он не вставляет все содержимое вектора - и много выкалывает на него, что делает его похожим на то, что векторные итераторы увеличиваются более чем на один шаг за раз, Это довольно сложно сказать, поскольку, поскольку это не происходит, если вы входите в отладчик.

Очевидный вывод для рисования будет заключаться в том, что он не является потокобезопасным, но я понимаю, что это должно быть потому, что единственная измененная переменная - это s, которая имеет локальную область видимости.

Существует немало вещей, которые помогут решить ближайшую проблему:

  • удалите параллель для
  • вызовите критический раздел вокруг вызова insert()
  • #define HAS_ITERATOR_DEBUGGING 1
  • замените одиночный вызов insert() ручным циклом и вставьте каждый элемент индивидуально (это в основном то, что эта функция делает внутренне, но проблема определенно исчезает, когда я делаю это сам)
  • создать 32-битную версию того же кода
  • создать версию выпуска того же кода

Это компилируется под MSVС++ 2008 SP1, с реализацией STL, поставляемой компилятором.

Может ли кто-нибудь пролить свет на то, что происходит здесь? Спасибо заранее за любые подсказки - я довольно тупой:)

Изменить: если это не ясно, я не ищу "быстрого исправления", чтобы этот код работал; как отмечалось выше, я знаю немало таких. Я хочу понять, почему эта проблема возникает в первую очередь.

Изменить 2: Код работает правильно, когда скомпилирован с помощью gcc.

Ответ 1

Похож на ошибку в реализации VS 2008 в OpenMP и/или STL. С VS 2010 он не воспроизводится; кроме того, он не воспроизводится с компилятором Intel и заголовками VS 2008, что заставляет меня думать, что ошибка более вероятна в поддержке OpenMP в компиляторе VС++.

РЕДАКТИРОВАТЬ: Код с первой приватой, которую я опубликовал ранее, не работает, даже с _SECURE_SCL = 1. Кажется, мне просто повезло.

Ответ 2

Я согласен с дархуком, я предлагаю отметить s private

#pragma omp parallel for private(s)

Ответ 3

Я никогда не писал никакого кода OpenMP, но я сделал много многопоточных программ, поэтому мои инстинкты могут быть или не быть правильными.

Метод insert() в наборе не является потокобезопасным, и он терпит неудачу, поскольку несколько потоков ссылаются на него одновременно. Вы должны заблокировать (или некоторый эквивалент OpenMP), чтобы гарантировать, что вставки выполняются последовательно.

EDIT: Попробуйте это

#include <omp.h>

#define _HAS_ITERATOR_DEBUGGING 0

#include <vector>
#include <set>


int main(int argc, const char* argv[]) {    
   std::vector<int> v;
   for(int j = 0; j < 20; ++j) {
      v.push_back(j);
   }


omp_lock_t writelock;
OMP_INIT_LOCK(&writelock);

   #pragma omp parallel for
   for(int i = 0; i < 100000; ++i) {
      std::set<int> s;
      std::vector<int>::const_iterator begin = v.begin(), end = v.end();
      OMP_SET_LOCK(&writelock);
      s.insert(begin, end); // after this line s should contain the numbers 0-19
      OMP_UNSET_LOCK(&writelock);
      if(s.size() != 20) {
         printf("fail\n");
         exit(3);
      }
   }
   return 0;
}