2012-12-19 62 views
16

我读过on Stackoverflow没有一个STL容器对于编写是线程安全的。但是这在实践中意味着什么?这是否意味着我应该在普通数组中存储可写数据?写std :: vector vs plain数组的线程安全

我预计并发调用std::vector::push_back(element)可能会导致不一致的数据结构,因为它可能需要调整向量的大小。但关于像这样的情况下,当调整大小不涉及什么:)使用阵列

1:使用`的std :: vector``

int data[n]; 
// initialize values here... 

#pragma omp parallel for 
for (int i = 0; i < n; ++i) { 
    data[i] += func(i); 
} 

2):

std::vector<int> data; 
data.resize(n); 
// initialize values here... 

#pragma omp parallel for 
for (int i = 0; i < n; ++i) { 
    data[i] += func(i); 
} 

在线程安全性和b)性能方面,第一个实现是否比第二个实现更好?我宁愿使用std :: vector,因为我对C风格的数组不太舒服。

编辑:我删除了#pragma omp atomic update保护写。

+1

我不能确定它是否成为答案,但我很确定写入'std :: vector'的不同元素是线程安全的。 – Angew

+1

这两个片段同样是线程安全的。 –

+0

“但这在实践中意味着什么?” :这意味着一个容器必须专门锁定写入*和*读取,如果任何/任一操作符合并发**写入**。您可以让所有读者在您想要的容器上敲击,但只要写入*潜力*被引入,所有投注都将关闭,您必须锁定*所有*存取权限(而不仅仅是其他作者)。顺便说一句,单写多读锁适用于此。 – WhozCraig

回答

22

两者同样安全。如果没有元素可以从多个线程访问,那你可以。你的并行循环只会访问每个元素一次,因此只能从一个线程访问。

容器的成员函数的标准中存在非线程安全的空间。在这种情况下,您使用vector<int>::operator[],所以您希望明确保证该成员的线程安全,因为即使在非常量向量上调用它也不会修改向量本身,这似乎是合理的。所以我怀疑在这种情况下存在问题,但我没有寻找保证[编辑:rici找到它]。即使它可能不安全,您可以在循环前执行int *dataptr = &data.front(),然后索引dataptr而不是data

另外,这个代码是而不是保证安全,因为它是一个特殊情况,其中多个元素共存于一个对象内。对于数组bool来说是安全的,因为它的不同元素是不同的“内存位置”(1.7在C++ 11中)。

+0

我写了一个小测试函数来查看openmp是否正常工作。我使用了'vector ',将其大小调整为使用的线程数(初始化为false),并将所有值同时设置为true。如果每个值都为真,则该函数返回true以显示所有线程正确产卵。 '矢量'再次袭击。幸运的是,实际上我最近阅读了标准的相关部分,所以我能够弄明白,但是如果有人发现这个问题,他们需要确保避免同时写入'vector '。 – Justin

+0

这样做,它应该是安全的push_back从每个相应的单独线程'std :: vector >'下的个别向量? –

1

它的主要意思是,如果你有多个线程访问vector,你不能依靠C++来阻止你使用多个并发写入来破坏数据结构。所以你需要使用某种警卫。另一方面,如果你的程序没有使用多个线程,就像你的例子似乎没有,你完全没问题。

+5

他的示例通过OpenMP使用多个线程 – nogard

+2

Upvoted为0.这可能是不正确的,但是在删除编辑之前发布:#pragma omp atomic update,它可以防止并发写入,并使得该答案在原帖 –

1

在这种情况下,你应该只用必要数量的值构造你的向量?一切都会正常工作。

std::vector<int> data(n, 0); 

resize()适用于。表现将是平等的。 多线程访问不会破坏向量的原因是:您的数据位于其位置,不会从那里移动。 OMP线程一次不会访问相同的元素。

17

对于指定数据竞争规则的C++ 11,描述了容器的线程安全性。该标准的相关章节是§ 23.2.2第2段:

尽管(17.6.5。9)时,需要实现以避免数据竞争,当同一序列中不同元素中包含的对象的内容(除了矢量bool >)被同时修改时。可以同时执行x [1] = 5和* x.begin()= 10的矢量<int> x,其大小大于1,但x [0]可以同时执行x [1] = 5和* x.begin()= 10。 = 5和* x.begin()= 10同时执行可能会导致数据竞争。作为一般规则的例外,对于矢量<和y,y [0] =真可以与y [1] =真正竞争。末端注]

所提§ 17.6.5.9通过任何标准的库接口基本上禁止任何并发修改,除非明确允许,所以我引用这一节将告诉你什么是允许的(包括你的使用)。

由于议题由史蒂夫杰索普的§ 23.2.2第1段提出明确允许在序列容器中的同时使用的[]

为了避免数据争用(17.6.5.9)的目的,实施方式中应考虑以下函数为const:开始,结束,rbegin,rend,front,back,data,find,lower_bound,upper_bound,equal_range,除了关联或无序关联容器,operator []。