【问题标题】:Write concurrently vector<bool>并发写向量<bool>
【发布时间】:2016-02-10 14:07:35
【问题描述】:

我知道可以从std::vector 并发读取而不会产生“坏”后果,因为此操作可以被认为是线程安全的。

但是对于写操作却不能这么说。但是,我想知道这是否总是正确的,例如考虑到我的特定情况。

我有一个std::vector&lt;bool&gt;,其中所有元素都初始化为false,并且,给定一个索引数组,我需要从false 更改这些元素的值(每个索引的vector[index])到true

如果我为每个索引使用不同的线程(并且某些索引可能具有相同的值),这个操作是否可以被认为是线程安全的?

如果向量是std::vector&lt;int&gt;(或任何原始类型)并且分配的值始终相同(例如 1),是否仍可以认为此操作是线程安全的?

【问题讨论】:

标签: c++ multithreading c++11 vector


【解决方案1】:

vector&lt;bool&gt; 的并发写入永远不会正常,因为底层实现依赖于vector&lt;bool&gt;::reference 类型的代理对象,它的作用就好像它是对bool 的引用,但实际上会根据需要获取和更新位域字节.

在不同步的情况下使用多个线程时,可能会发生以下情况:线程 1 应该更新一点,并读取包含它的字节。然后线程 2 读取相同的字节,然后线程 1 更新一个位并将字节写回,然后线程 2 更新另一个位并将字节写回,覆盖线程 1 的编辑。

这只是一种可能的情况,还有其他情况会导致相同类型的数据损坏。


vector&lt;int&gt; 的情况下,如果您绝对确定所有线程将相同的值写入向量,那么此操作通常不会导致数据损坏。但是,该标准当然总是格外小心,并将对内存位置的所有并发访问(其中至少一个是写访问)定义为未定义行为:

如果其中一个修改了内存位置,而另一个读取或修改了相同的内存位置,则两个表达式计算会发生冲突。 – intro.races/2

因此,一旦您从两个不同的线程对同一元素进行任何修改操作,您就会遇到竞争条件并需要适当的同步,例如通过使用std::atomic&lt;int&gt;

【讨论】:

  • "在vector的情况下,如果你绝对确定所有线程都将相同的值写入vector,那么这个操作永远不会导致数据损坏。" – 这是不正确的,对同一内存位置的并发写入会导致未定义的行为,无论要写入的值是否相同。引用(intro.races/2) 的话:“如果其中一个修改内存位置而另一个读取或修改相同的内存位置,则两个表达式计算会发生冲突。”
  • 澄清一下……这适用于在同一索引处写入相同的值。如果所有线程都写入不同的索引,就像实际问题的一部分一样,内存位置将是分开的,并且没有数据竞争。
  • @ArneVogel 嗯,标准当然总是格外小心......你当然不会期望在几乎任何系统上都以非原子方式编写int。尽管如此,我还是把这句话放在我的回答中
  • @FelixDombek 如果您依赖相关标准中没有的期望,那么如果这些期望在未来被证明是错误的,那么您的代码就会严重崩溃。在经历了足够多的痛苦之后,你会认为我们会开始学习避免它。
  • @FelixDombek - 如果我使用大小为一个字节(而不是一个字)的“char”或“uint8_t”类型的向量,它是否是线程安全的
【解决方案2】:

[container.requirements.dataraces]/2 说:

尽管有 (17.6.5.9),当同时修改同一容器中不同元素中包含的对象的内容时(vector&lt;bool&gt; 除外),实现需要避免数据争用。 p>

因此,您可以安全地从不同线程修改同一标准库容器的不同元素,除了当该容器为 vector&lt;bool&gt; 时。

【讨论】:

    猜你喜欢
    • 2010-10-14
    • 1970-01-01
    • 2014-09-19
    • 2019-04-18
    • 1970-01-01
    • 1970-01-01
    • 2018-02-16
    • 2015-10-09
    相关资源
    最近更新 更多