C++ 是否保证从两个线程访问数组的相邻元素是安全的

gig*_*tes 5 c++ multithreading thread-safety language-lawyer c++11

就 C++ 标准而言(我猜是 C++11 及更高版本,因为在没有考虑线程之前),并发写入数组的不同元素(可能是相邻元素)是否安全?

例如:

#include <iostream>
#include <thread>

int array[10];

void func(int i) {
   array[i] = 42;
}

int main() 
{
   for(int i = 0; i < 10; ++i) {
      // spawn func(i) on a separate thread
      // (e.g. with std::async, let me skip the details)
   }
   // join

   for(int i = 0; i < 10; ++i) {
      std::cout << array[i] << std::endl; // prints 42?
   }

   return 0;
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,语言是否保证数组的不同元素的写入不会导致竞争条件?是否保证任何类型,或者是否有任何安全要求?

luk*_*k32 4

是的。

来自https://en.cppreference.com/w/cpp/language/memory_model

当一个表达式的计算写入某个内存位置而另一个计算读取或修改同一内存位置时,这些表达式被称为冲突。具有两个相互冲突的评估的程序会发生数据竞争,除非 [...]

然后:

一个内存位置

  • 标量类型(算术类型、指针类型、枚举类型或 std::nullptr_t)的对象
  • 或非零长度的最大连续位域序列

因此,如果数组的元素存储在不同的内存位置,则不会出现冲突的计算。

数组是

形式的声明T a[N];将 a 声明为由 N 个连续分配的 T 类型对象组成的数组对象。

由于两个不同的对象不能具有相同的地址,因此它们及其组成部分不能具有相同的内存位置。这保证了先前的要求的满足。

此外,对象可以由多个内存位置组成,因此您甚至可以让两个线程对同一对象的不同成员进行操作!

请注意,为了使您的示例正确, join 也必须正确编写,但它与数组的相邻元素无关,而是对同一个元素进行操作,所以我想这超出了问题的范围。


个人说明:顺便说一句。如果不能保证这一点,如果不在标准库中渲染无用的并行计算,就会严重限制。