std::vector push_back 在并行 for 循环中使用时失败

std::vector push_back fails when used in a parallel for loop

本文关键字:循环 失败 for 并行 push vector back std      更新时间:2023-10-16

>我有一个代码如下(简化代码(:

for( int i = 0; i < input.rows; i++ )
{
    if(IsGoodMatch(input[I])
    { 
        Newvalues newValues;
        newValues.x1=input.x1;
        newValues.x2=input.x1*2;
        output.push_back( newValues);
    }
} 

这段代码效果很好,但是如果我想使用 omp parallel 使其并行,我在output.push_back上遇到错误,似乎在矢量调整大小期间,内存损坏了。

问题是什么,我该如何解决?

如何确保任何时候只有一个线程将新项目插入 vector?

简单的答案是std::vector::push_back不是线程安全的。

为了安全地并行执行此操作,您需要同步以确保不会同时从多个线程调用push_back

C++11 中的同步可以通过使用 std::mutex 轻松实现

std::vectorpush_back不能保证像现在这样以并发方式调用时的正确行为(没有线程安全(。

但是,由于元素不相互依赖,因此resize向量并分别修改循环中的元素是非常合理的:

output.resize(input.rows);
int k = 0;
#pragma omp parallel for shared(k, input)
for( int i = 0; i < input.rows; i++ )
{
    if(IsGoodMatch(input[I])
    { 
        Newvalues newValues;
        ...
        // ! prevent other threads to modify k !
        output[k] = newValues;
        k++;
        // ! allow other threads to modify k again !
    }
} 
output.resize(k);

由于使用 operator[] 的直接访问不依赖于std::vector的其他成员,这可能会导致线程之间的不一致。但是,此解决方案可能仍需要显式同步(即使用互斥锁等同步机制(,以确保使用正确的k值。

"我怎样才能确保任何时候只有一个线程将新项目插入 vector?">

你不需要。线程将修改不同的元素(驻留在内存的不同部分(。您只需要确保每个线程尝试修改的元素是正确的。

使用并发向量

#include <concurrent_vector.h>

Concurrency::concurrent_vector<int> C++11 中。

它是矢量的线程安全版本。

push_back之前放一个#pragma omp critical

我通过派生标准std::vector类来解决了一个类似的问题,只是为了实现一个适合在OpenMP范式中工作的atomic_push_back方法。

这是我的"OpenMP-safe"矢量实现:

template <typename T>
class omp_vector : public std::vector<T>
{
    private:
    omp_lock_t lock;
    public:
    omp_vector()
    {
         omp_init_lock(&lock);
    }
    void atomic_push_back(T const &p)
    {
        omp_set_lock(&lock);
        std::vector<T>::push_back(p);
        omp_unset_lock(&lock);
    }
};

当然,您必须包括omp.h.然后,您的代码可能如下所示:

opm_vector<...> output;
#pragma omp parallel for shared(input,output)     
for( int i = 0; i < input.rows; i++ )
{
    if(IsGoodMatch(input[I])
    { 
        Newvalues newValues;
        newValues.x1=input.x1;
        newValues.x2=input.x1*2;
        output.atomic_push_back( newValues);
    }
}

如果你仍然需要代码的非并行部分中其他地方的output向量,你可以使用普通的push_back方法。

您可以尝试使用互斥锁来解决问题。通常我更喜欢自己实现这样的事情;

static int mutex=1;
int signal(int &x)
{
    x+=1;
    return 0;
}
int wait(int &x)
{
    x-=1;
    while(x<0);
    return 0;
}
for( int i = 0; i < input.rows; i++ )
{
    if(IsGoodMatch(input[I])
    {
        Newvalues newValues;
        newValues.x1=input.x1;
        newValues.x2=input.x1*2;
        wait(mutex);
        output.push_back( newValues);
        signal(mutex);
    }
} 

希望这能有所帮助。