在没有锁定的x86机器上读/写一个int

假设在C程序中,我有一个在32位机器上运行的P线程,以及int MAX一个共享的32位整数

每个线程都可以读/写MAX。

要求:线程读取的值不应该被破坏,例如,前16位和后16位不同步

问题:我是否需要锁来保护读写? 或者我可以安全地忽略锁定,因为保证LOAD / SAVE汇编指令primefaces地发生吗?

当int正确对齐时,读取和写入是primefaces的。 它无法跨越缓存行的末尾。 高速缓存行是64字节。 大多数编译器都会确保对齐得到处理,但是可以用结构打包编译指示覆盖它。

是的,当线程执行读 – 修改 – 写操作时,您需要一个锁来保护该值。 你可以从InterlockedXxxx获得一个便宜的。

这里的问题是read-modify-write。

假设您有两个单独的物理核心(它们可以位于相同的物理包中)。 他们都读了。 第一个核心修改 – 也就是说,增加当前保存在寄存器中的值。 此时,修改开始传播到缓存 – 但同时第二个核心也修改了值,并开始将缓存传播到缓存。

您丢失了其中一个更新。

高速缓存一致性协议不处理多个并发写入的情况。 没有什么能够让一个核心等待它的写入,因为另一个核心也是 – 当前正在写入 – ; 因为这些信息在核心之间根本不公开。 他们 – 不能这样做。

它们确实处理多个连续写入,例如在核心外部总线引脚上看到更改后(例如,成为公共知识,而不是内核)。

另一个问题是指令重新排序。 这些线程 – 如果它们在不同的内核上运行,它们的指令重新排序将不会注意其他线程正在做什么; 只针对特定线程正在做的事情。

想象一下,一个线程将写入值然后设置一个标志。 另一个线程将看到引发的标志,然后读取该值。 这些线程,如果在单独的核心上,将仅针对自己重新排序其指令流 – 核心不会考虑其他线程,因为它们不能 – 他们不了解它们。

因此,在第一个线程中,可以在写入值之前重新排序标志设置 – 毕竟,对于该线程 ,重新排序是正常的。 这两个变量完全断开。 没有排序依赖。 依赖存在于另一个不同核心的线程中,因此我们的核心无法知道它。

另一个线程当然会看到提升和读取的标志,即使写入实际上还没有发生。

不,字大小的载荷和存储是primefaces的。 但是,您希望执行添加或其他算法,因为它们可能使用读取和写入,因此需要锁定。

不同的体系结构/ CPU具有不同的primefaces性保证。 C旨在是可移植的,因此您不应对任何特定体系结构/ CPU所做的primefaces性保证做出任何假设。 这意味着即使对于primefaces读/写,您也应该使用某种抽象(例如,为每个不同的体系结构提供必要的primefaces操作的库)。

据我所知(这不是很多 – 我的经验大部分只是80×86),对于大多数架构来说,对小于某个最小大小的对齐地址的读写通常保证是primefaces的(其中“有些”最小大小“可以是通用寄存器的大小,高速缓存行的大小,或其他东西)。

这不包括修改(例如,读取,修改,然后写入地址的指令/操作)。 对于“int MAX”变量(与“const int MAX = 1234”相反),我假设您想要执行类似“if(foo> MAX)MAX = foo”的操作。 并且你需要一个更强大的primefaces操作(例如,如果比较为假,则可以在循环中重新进行primefaces“比较和交换”)。

另外,不要忘记将变量声明为“volatile”。

  • 布伦丹

大多数(全部?)现代CPU都有特殊指令,可以保证对这类变量的primefaces访问。 C现在具有这些指令的标准化接口。 不幸的是,大多数编译器还没有完全实现这一点。

您应该使用扩展。 gcc例如有一大堆 。 如果你在网络上看起来很棒,你也应该很容易找到可以与其他编译器一起使用的内联汇编指令的实现。

我不确定是否有任何明确的保证,但只要该值存储在正确对齐的内存位置(即存储器地址是4的倍数),那么在实践中你会没事的; 内存仅以至少该大小的块读/写。