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