使用两个线程在C / C ++中操作不同的数组索引时,是否需要同步?
假设我有一个如下定义的数组:
volatile char v[2];
我有两个线程(分别用A,B表示)操纵数组v
。 如果我确保A,B在任何时候使用不同的索引,也就是说,如果A现在正在操纵v[i]
,那么B要么什么都不做,要么操纵v[1-i]
。 我想知道这种情况需要同步吗?
我已经提到了这个问题 ,但我认为它在Java中是有限的。 我之所以提出这个问题的原因是,我在一个大型项目中一直在努力解决一个奇怪而罕见的错误,直到现在,我能解释这个错误的唯一原因是需要同步以上操纵。 (由于这个bug非常罕见,我很难certificate我的推测是否属实)
编辑:对于v
,可以进行读取和修改。
它可能是编译器错误或硬件限制。
有时,当从内存中访问少于32位/ 64位的变量时,处理器将读取32位,将apprpriate设置为8位或16位,然后写回整个寄存器。 这意味着它也会读/写相邻的内存,导致数据竞争。
解决方案是
-
使用字节访问指令。 它们可能不适用于您的处理器,或者您的编译器不知道使用它们。
-
填充你的元素,以避免这种共享。 如果目标平台不支持字节访问,编译器应自动执行此操作。 但是在一个数组中,这与内存布局reqiurements冲突。
- 同步整个结构
C ++ 03 / C ++ 11辩论
在经典的C ++中,您应该避免/减轻这种行为。 在C ++ 11中,这违反了memry模型的要求,如其他答案中所述。
就C ++ 11和C11标准而言,您的代码是安全的。 C ++11§1.7[intro.memory] / p2,省略了无关的注释:
存储器位置是标量类型的对象或者具有非零宽度的相邻位域的最大序列。 两个或多个执行线程(1.10)可以更新和访问单独的存储器位置,而不会相互干扰。
char
是一个整数类型,这意味着它是一个算术类型,这意味着volatile char
是一个标量类型,因此v[0]
和v[1]
是不同的内存位置。
C11在§3.14中有类似的定义。
在C ++ 11和C11之前,语言本身没有线程的概念,因此您将受到正在使用的特定实现的左右。
只有在访问同一内存并进行修改时,才需要处理同步。 如果您只是阅读,那么您也不需要关心同步。
正如您所说,每个线程将访问不同的索引,那么您不需要在此处进行同步。 但是你需要确保两个线程不应该同时修改同一个indice。