Omp для вектора инициализации цикла - PullRequest
0 голосов
/ 09 марта 2019

У меня есть следующий код:

int main() {
vector<int> vec;

#pragma omp parallel for ordered schedule(dynamic)            
for (int i = 0; i <= 300; i++) {
    vec.push_back(i);
} 
cout << vec.size() << endl;
}

Размер вектора иногда равен 285 или 294, но никогда не равен 301. Что я делаю не так?

1 Ответ

2 голосов
/ 09 марта 2019

Здесь вы видите эффект небезопасной функции потока, вызываемой несколькими потоками.Внутренне push_back делает что-то вроде следующего в псевдокоде

if reallocation needed:
    reallocate
construct new object at &data[size]
++size

Теперь попробуйте представить различные потоки, выполняющие вышеуказанный код одновременно.Что произойдет, если оба потока увидят необходимость перераспределения и попыток сделать это одновременно.Что если они оба построят объект на &data[size], потому что они оба достигли этой точки до ++size?Обратите внимание, что даже попытка увеличения на той же строке, что и конструкция, не будет работать, потому что они по-прежнему являются отдельными неатомарными операциями.

Что вы действительно хотите сделать, так это создать цикл строго потокобезопасных операций, таких какследующий.

int main() {
    std::vector<int> vec(301);

    #pragma omp parallel for
    for (int i = 0; i <= 300; i++) {
        vec[i]= i;
    }
    std::cout << vec.size() << std::endl;
}

В этом случае каждый поток обращается к vec[i] с уникальным i.Таким образом, никакие операции не происходят одновременно с одними и теми же объектами.Это абсолютно безопасно.

Чтобы ответить на ваш дополнительный вопрос, нельзя одновременно ввести push_back в вектор.Вам нужно будет синхронизировать ваши вызовы push_back, что сделает их медленнее, чем непараллельный способ.Другое решение - заполнить локальные контейнеры потоков, а затем объединить их.Но всякий раз, когда простое решение, которое я показал выше, применимо, оно также будет быстрее, чем альтернативы.

...