我想我理解你的意思了。这里有三个因素。
C++11标准描述了在语言层面上发生的事情...锁定std::mutex
是一种同步操作。C++标准没有描述它是如何工作的。就C++标准而言,CPU缓存不存在。
C++实现在某个时刻将一些机器码放入您的应用程序中,以实现互斥锁。创建此实现的工程师必须考虑C++11规范和架构规范。
CPU本身以这样的方式管理缓存,以提供C++实现所需的语义。
如果您查看原子操作,可能更容易理解,因为它们转换为更小的汇编代码片段,但仍提供同步。例如,请在GodBolt上尝试以下操作:
#include <atomic>
std::atomic<int> value;
int acquire() {
return value.store(std::memory_order_acquire);
}
void release() {
value.store(0, std::memory_order_release);
}
您可以查看汇编代码:
acquire():
mov eax, DWORD PTR value[rip]
ret
release():
mov DWORD PTR value[rip], 0
ret
value:
.zero 4
在x86上,没有必要做什么,CPU已经提供了所需的内存排序语义(尽管可以使用显式的
mfence
,它通常由操作隐含)。但并非所有处理器都是如此,参见Power输出:
acquire():
.LCF0:
0: addis 2,12,.TOC.-.LCF0@ha
addi 2,2,.TOC.-.LCF0@l
addis 3,2,.LANCHOR0@toc@ha # gpr load fusion, type int
lwz 3,.LANCHOR0@toc@l(3)
cmpw 7,3,3
bne- 7,$+4
isync
extsw 3,3
blr
.long 0
.byte 0,9,0,0,0,0,0,0
release():
.LCF1:
0: addis 2,12,.TOC.-.LCF1@ha
addi 2,2,.TOC.-.LCF1@l
lwsync
li 9,0
addis 10,2,.LANCHOR0@toc@ha
stw 9,.LANCHOR0@toc@l(10)
blr
.long 0
.byte 0,9,0,0,0,0,0,0
value:
.zero 4
这里明确指出了isync
指令,因为Power内存模型在没有它们的情况下提供的保证更少。
然而,这只是将问题推到了一个更低的级别。CPU本身使用类似MESI协议的技术来管理共享缓存,这是一种维护缓存一致性的技术。
在MESI协议中,当一个核心修改一个缓存块时,它必须从其他缓存中刷新该块。其他核心将该块标记为无效,并在必要时将其内容写入主存储器。这很低效,但是必要的。因此,您不希望尝试将一堆常用的互斥锁或原子变量塞在一个小的内存区域中,因为您可能会导致多个核心争夺相同的缓存块。维基百科文章非常全面,比我写在这里的内容更详细。
我忽略的一些内容是,互斥锁通常需要某种内核级别的支持,以便线程可以睡眠或唤醒。