简体   繁体   English

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

[英]Does C++ guarantees it is safe to access adjacent elements of an array from two threads

As far as the C++ standard is concerned (C++11 and later, I guess, since before threads were not considered), is it safe to write concurrently to different , possibly adjacent, elements of an array? 就C ++标准而言(C ++ 11及更高版本,我猜,因为在不考虑线程之前),并发写入数组的不同的 ,可能相邻的元素是否安全?

For example: 例如:

#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;
}

In this case, is it guaranteed by the language that the writes of different elements of the array do not cause race conditions? 在这种情况下,语言是否保证数组的不同元素的写入不会导致竞争条件? And is it guaranteed for any type, or are there any requirements for this to be safe? 它是否适用于任何类型,或者是否有任何安全要求?

Data races only occur on the same memory location, ie there can be a data race on two glvalues x and y only if &x == &y . 数据竞争仅发生在相同的内存位置,即只有当&x == &y才能在两个glvalues xy上进行数据竞争。

[intro.races]/2 [intro.races] / 2

Two expression evaluations conflict if one of them modifies a memory location and the other one reads or modifies the same memory location. 如果其中一个修改内存位置而另一个读取或修改相同的内存位置,则两个表达式评估会发生冲突。

[intro.races]/21 [intro.races] / 21

The execution of a program contains a data race if it contains two potentially concurrent conflicting actions [...] 如果一个程序的执行包含两个可能同时发生冲突的动作,那么该程序的执行包含数据竞争[...]

The remainder of which doesn't apply here. 其余部分不适用于此处。 So no there is no data race on your array. 所以你的阵列没有数据竞争。

Yes. 是。

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

When an evaluation of an expression writes to a memory location and another evaluation reads or modifies the same memory location, the expressions are said to conflict. 当表达式的评估写入内存位置而另一个评估读取或修改相同的内存位置时,表达式会发生冲突。 A program that has two conflicting evaluations has a data race unless [...] 具有两个相互冲突的评估的程序具有数据竞争,除非[...]

Then: 然后:

A memory location is 内存位置

  • an object of scalar type (arithmetic type, pointer type, enumeration type, or std::nullptr_t) 标量类型的对象(算术类型,指针类型,枚举类型或std :: nullptr_t)
  • or the largest contiguous sequence of bit fields of non-zero length 或非零长度的最大连续的位域序列

So, if the elements of an array are stored at different memory locations you do not have a conflicting evaluations. 因此,如果数组的元素存储在不同的内存位置,则不会产生冲突的评估。

And an array is: 一个数组是:

A declaration of the form T a[N]; 表格T a[N]; , declares a as an array object that consists of N contiguously allocated objects of type T. ,将a声明为一个数组对象,由N个连续分配的T类型对象组成。

Since two distinct objects cannot have the same address, they and their constituants cannot have the same memory location. 由于两个不同的对象不能具有相同的地址,因此它们及其组成者不能具有相同的存储位置。 Which guarantees satisfaction of the earlier requirement. 这保证了对早期要求的满意度。

Moreover, objects can consist of more than one memory location, so you could even have two threads operate on different members of the same object! 此外,对象可以包含多个内存位置,因此您甚至可以让两个线程对同一对象的不同成员进行操作!

Please note that for your example to be correct, join has to be written correctly as well, but it's not related to adjacent elements of an array, but rather operating on the same one, so I guess it's beyond the scope of the question. 请注意,为了使您的示例正确,连接也必须正确编写,但它与数组的相邻元素无关,而是在同一个元素上运行,所以我想它超出了问题的范围。


Personal note: Btw. 个人说明:顺便说一句。 If this wasn't guaranteed, it would seriously limit if not render useless parallel computing in standard library. 如果不能保证这一点,那么如果不在标准库中进行无用的并行计算,则会严重限制。

Yes, but 'OK' does not mean it is smart to do it. 是的,但“确定”并不意味着这样做很聪明。

There are several issues to consider, perhaps the most important one is CPU caches. 有几个问题需要考虑,也许最重要的一个是CPU缓存。 On eg x86, cache lines are 64 bytes long, so each thread should eg work on a chunk of the array that matches the cache line length to avoid eg false sharing. 在例如x86上,高速缓存行是64字节长,因此每个线程应该例如在与高速缓存行长度匹配的阵列块上工作以避免例如错误共享。

Here is one example: false sharing SO question/answer 这是一个例子: 假共享SO问题/答案

It's safe to concurrently access consecutive elements on separate threads , but if it occurs frequently, it could cause performance issues in the code. 并行访问不同线程上的连续元素是安全的 ,但如果频繁发生, 则可能导致代码中出现性能问题。 This is due to the fundamental limitations of parallelism on modern CPUs. 这是由于现代CPU上并行性的基本限制。

For most programs memory access is a major bottleneck. 对于大多数程序来说,内存访问是一个主要的瓶颈。 Performance-critical sections of the code have to be written carefully to avoid excessive cache misses. 必须仔细编写代码的性能关键部分,以避免过多的缓存未命中。 There are multiple levels to the cache, and each level is faster than the previous one. 缓存有多个级别,每个级别比前一个级别更快。 However, when data is not in the cache, or when data might have been changed by another CPU, it has to be re-loaded into the cache. 但是,当数据不在缓存中时,或者数据可能已被另一个CPU更改时,必须将其重新加载到缓存中。

The CPU can't keep track of the state of each individual byte, so instead it keeps track of blocks of bytes called Cache Lines. CPU无法跟踪每个字节的状态,因此它会跟踪称为高速缓存行的字节块。 If any byte in a cache line is changed by a separate CPU, it has to be re-loaded to ensure synchronization. 如果高速缓存行中的任何字节由单独的CPU更改,则必须重新加载以确保同步。

Accessing separate bytes on different threads only causes this re-loading if they're in the same cache line. 在不同线程上访问单独的字节只会导致重新加载,如果它们位于同一缓存行中。 And because cache lines are contiguous, accessing contiguous elements from separate threads will usually result in the memory having to be re-loaded into the cache. 并且因为高速缓存行是连续的,所以从不同的线程访问连续的元素通常会导致必须将存储器重新加载到高速缓存中。 This is called false sharing, and should be avoided in parallel code if performance is a concern. 这称为虚假共享,如果性能受到关注,应在并行代码中避免使用。

That being said, if it happens rarely it's probably fine, and you should benchmark and test your code before optimizing it. 话虽这么说,如果很少发生它可能很好,你应该在优化它之前对你的代码进行基准测试和测试。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 C ++编译器如何确保在不同的线程上安全地使用不同但相邻的内存位置? - What does the C++ compiler do to ensure that different but adjacent memory locations are safe to be used on different threads? C ++从两个线程访问Int变量。 线程安全吗? - C++ access to Int variable from two threads. Thread safe? 在C ++中通过多个线程对数组的各个单元格进行修改是否安全? - Is modification of various cells of an array by many threads safe in c++ (boost) 通过 c++ 中的迭代器从向量中获取相邻的元素对 - Get adjacent pairs of elements from vector via iterator in c++ C ++从多个线程访问矢量 - C++ Access to vector from multiple threads C ++运算符%保证 - C++ operator % guarantees C ++标准为缩小从double到int的转换提供了什么保证? - What guarantees does the C++ standard give for narrowing conversion from double to int? 如何使用C ++中的COM对象返回的VARIANT数据类型访问SAFE ARRAY? - How do you access to SAFE ARRAY in a VARIANT data type returned from a COM object in C++? C ++对字符文字的排序有什么保证? - What guarantees does C++ make about the ordering of character literals? 在c ++中访问地图的相邻元素 - Accessing adjacent elements of a map in c++
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM