float myfloats[2];
// thread A:
myFloats[0] = 1.0;
// thread B:
myFloats[1] = 1.0;
假设线程A将始终访问索引0,线程B索引1.这是安全的还是数组可能被损坏?
答案 0 :(得分:5)
C11 n1570草案标准似乎断言这是安全,但我断言它不明智。
我的论点基于这样一个事实:数组的元素不能在内存中重叠,并且在C11草案标准的以下条款中。
5.1.2.4 多线程执行和数据竞赛
4。两个表达式评估冲突如果其中一个修改了内存位置而另一个读取或修改了相同的内存位置。
25。程序的执行包含数据竞争,如果它在不同的线程中包含两个冲突的动作,其中至少有一个不是原子的,并且都不会发生在另一个之前。任何此类数据竞争都会导致未定义的行为。
27。注13:引入可能由抽象机器修改的潜在共享内存位置的编译器的编译器转换通常被本标准排除,因为在抽象机器执行的情况下,这样的分配可能会覆盖另一个线程的另一个分配。没有遇到过数据竞争。这包括覆盖不同内存位置中相邻成员的数据成员分配的实现。在有问题的原子可能混淆的情况下,我们通常也会排除原子载荷的重新排序,因为这可能违反了可见的序列"规则。
28。注14:引入潜在共享内存位置的推测性读取的转换可能无法保留本标准中定义的程序语义,因为它们可能引入数据争用。但是,它们通常在优化编译器的上下文中有效,该编译器针对具有明确定义的数据争用语义的特定计算机。对于不能容忍比赛或提供硬件竞赛检测的假想机器,它们无效。
我们在这里了解到,两个线程在同一个内存位置执行冲突操作是UB,但编译器通常是排除在外的#34;从引入任务到可能共享的#34;抽象机器不会执行的内存位置。
您断言您的线程只能在自己的特定索引处访问(读取和写入)元素。由于毫无疑问他们无法访问相同的内存位置,因此在我看来,如果您遇到所有其他限制,例如正确对齐float
个变量,您所做的事情是安全的
但是,我按照你的建议查询智慧。由于这两个内存位置是连续的,因此您可能会遇到严重的错误共享问题。这是因为CPU通常将内存缓存在" line"大约32或64个连续字节,并使用MESI协议传递缓存状态。
如果在一个核心上运行的线程在此高速缓存行中执行任何,则其他核心中的高速缓存行的所有副本及其中包含的所有内容都无效,通常导致其他核心上的线程需要从主内存重新读取更新的副本。这比从缓存访问慢几倍。
如果相关线程都访问了缓存行的相同部分,则会发生真正的共享,因为这种失效被证明是为了防止通信线程使用陈旧数据。
另一方面,如果线程都访问同一个高速缓存行的不同部分,则会发生 false sharing 。在这种情况下,无需进行无效,但硬件无论如何都是这样做的,因为访问彼此接近,惩罚所有这些。