对 std::vector 元素的赋值是线程安全的吗?

Posted

技术标签:

【中文标题】对 std::vector 元素的赋值是线程安全的吗?【英文标题】:Is assignment to a std::vector element thread-safe? 【发布时间】:2017-05-14 06:05:38 【问题描述】:

我创建了一个线程池,它同时写入同一个向量。

这个实现线程安全吗?

如果不是,我应该如何解决?

std::vector<double> global_var;

void func1(int i)

    global_var[i]=some_computation(i /* only depends on i */);


void load_distribution()

    const int N_max=100;
    global_var.assign(N_max,0.0);
    std::vector<std::thread> th_pool;
    for(long i=0;i<N_max;i++)
        th_pool.push_back(std::thread(func1,i));

    for(std::thread& tp : th_pool)
        tp.join();


更新

在所有线程终止之前,global_var 不会被程序的任何其他部分触及。

【问题讨论】:

虽然我不想承认,但当前的 sn-p 实际上是线程安全的。 ...不过,你的设计真的很脆弱。您的代码的另一部分可能会修改global_var,这将引入未定义的行为 @WhiZTiM 我保证,在所有线程终止之前,global_var 不会被程序的任何其他部分更改。 完整代码here以防万一。 【参考方案1】:

假设您的 global 向量没有被任何其他代码部分修改,那么您的代码是线程安全的。

每个线程都将写入(访问)向量的不同单元格,因此不存在“脏更新”问题。

此外,向量的类型是双精度型,在现代架构中大于 WORD 型。所以每个数组单元格之间不会相互重叠。

【讨论】:

非常感谢。如果我使用vector&lt;complicatedObj&gt; 会怎样。有什么问题吗? @ar2015 如果内存以对齐方式分配,则不会 什么是对齐方式的内存分配或什么是非对齐方式的例子? @ar2015 几个月前我已经回答了这个问题。如果你想看here 正如您所说,如果我使用vector&lt;complicatedObj&gt;,其中complicatedObj 本身包含多个vectors,则代码可能会失败。我说的对吗?【参考方案2】:

[container.requirements.dataraces]/1-2:

1 为避免数据争用 ([res.on.data.races]), 实现应将以下函数视为constbeginendrbeginrendfrontbackdata、[...]、at 和,除了关联或无序 关联容器,operator[]

2 尽管有 ([res.on.data.races]),但仍需要实现 当包含对象的内容在 同一个容器中的不同元素,除了vector&lt;bool&gt;,是 同时修改。

【讨论】:

【参考方案3】:

一般来说,std::vector 不是线程安全的,但上面的代码可以工作,因为支持数组是使用assign() 方法预先分配的。

只要编写者不重新分配后备数组,代码就可以工作。 assign() 方法会预先分配足够的空间,因此线程写入时不会发生这种情况。

【讨论】:

其实一个assignment已经用过了global_var.assign(N_max,0.0); 如果我使用 vector&lt;object&gt; 代替 vector&lt;double&gt; 会怎样,它内部将包含其他向量。在这种情况下,赋值会使向量无效吗? @ar2015 :不,它不会修改外部向量,它只会触及每个元素的内容。并且由于外部支持数组不会被修改,特定元素的修改将是线程安全的。当然,只要只有一根线接触它们。

以上是关于对 std::vector 元素的赋值是线程安全的吗?的主要内容,如果未能解决你的问题,请参考以下文章

C ++ - std :: vector安全多线程

通过线程共享指针的 std::vector

如何安全地引用 std::vector 元素?

std :: vector是否将其值类型的赋值运算符用于push_back元素?

我可以制作一个线程安全的 std::atomic<vector<int>> 吗?

为 std::vector 键入安全索引值