当用两个线程在C/C++中操作不同的数组索引时,是否需要同步

When manipulating different array indices in C/C++ with two threads, is synchronization needed?

本文关键字:数组 索引 同步 是否 操作 两个 线程 C++      更新时间:2023-10-16

假设我有一个定义如下的数组:

volatile char v[2];

我有两个线程(分别用A和B表示)在操作数组v。如果我确保A、B在任何时候都使用不同的索引,也就是说,如果A现在正在操纵v[i],那么B要么什么都不做,要么操纵v[1-i]。我想知道这种情况是否需要同步?

我已经提到了这个问题,但我认为它在Java中是有限的。我之所以问这个问题,是因为我已经在一个大型项目中与一个奇怪而罕见的错误斗争了好几天,到目前为止,我唯一能解释这个错误的原因是,上述操作需要同步。(由于这个错误非常罕见,我很难证明我的猜测是否属实)

编辑:v可以读取和修改。

就C++11和C11标准而言,您的代码是安全的。C++11§1.7[介绍内存]/p2,省略了无关注释:

内存位置是标量类型的对象或最大全部具有非零宽度的相邻比特字段的序列。两个或更多执行线程(1.10)可以更新和访问单独的内存位置而不会相互干扰。

char是一个积分类型,这意味着它是一个算术类型,也意味着volatile char是一个标量类型,所以v[0]v[1]是单独的存储位置。

C11在§3.14中有类似的定义。

在C++11和C11之前,该语言本身没有线程的概念,因此您只能任由所使用的特定实现摆布。

这可能是编译器错误或硬件限制。

有时,当从内存访问小于32位/64位的变量时,处理器会读取32位,设置相应的8或16位,然后写回整个寄存器。这意味着它也将读取/写入相邻的内存,从而导致数据竞争。

解决方案是

  • 使用字节访问指令。它们可能不适用于您的处理器,或者您的编译器不知道如何使用它们。

  • 填充元素以避免这种共享。如果目标平台不支持字节访问,编译器应该自动执行。但在数组中,这与内存布局要求相冲突。

  • 使整个结构同步

C++03/C++11辩论

在经典C++中,避免/减轻这种行为取决于您。在C++11中,这违反了内存模型的要求,正如其他答案中所述。

只有当您访问并修改同一内存时,才需要处理同步。如果您只是在读取,那么也不需要关心同步。

正如您所说的,每个线程将访问不同的索引,那么您在这里不需要同步。但您需要确保两个线程不应同时修改相同的标记。