这是一个锁,一次只能由一个执行线程持有。另一个执行线程尝试获取锁会导致后一个循环,直到锁被释放。
当两个线程试图同时获取锁时,它是如何处理的?
我认为这个问题也适用于其他各种互斥体实现。
这是一个锁,一次只能由一个执行线程持有。另一个执行线程尝试获取锁会导致后一个循环,直到锁被释放。
当两个线程试图同时获取锁时,它是如何处理的?
我认为这个问题也适用于其他各种互斥体实现。
正如上一张海报所指出的,每一种现代机器类型都有一种特殊的指令类别,称为“原子”,它们确实像上一张海报所表明的那样运行……它们至少针对指定的内存位置序列化执行。
在 x86 上,有一个 LOCK 汇编器前缀,指示机器应该以原子方式处理下一条指令。当遇到指令时,x86 上会发生几件事情。
对于 x86,有两个常用的指令用于实现锁。
uint32 cmpxchg(uint32 *memory_location,uint32 old_value,uint32 new_value){ 原子地{ if (*memory_location == old_value) *memory_location = new_value; 返回旧值; } }
uint32 xchg(uint32 *memory_location, uint32 new_value) { 原子地{ uint32 old_value = *memory_location; *memory_location = new_value; 返回 *old_value; } }
所以,你可以像这样实现一个锁:
uint32 mylock = 0; 而 (cmpxchg(&mylock, 0, 1) != 0) ;
我们自旋,等待锁,因此,自旋锁。
现在,解锁的指令不会表现出这些好的行为。根据您使用的机器,使用未锁定的指令,可以观察到各种违反一致性的行为。例如,即使在具有非常友好的内存一致性模型的 x86 上,也可以观察到以下情况:
线程 1 线程 2 移动 [w], 0 移动 [x], 0 mov [w], 1 mov [x], 2 mov eax, w mov eax, x mov [y], eax mov [z], eax
在这个程序结束时,y 和 z 的值都可以是 0!.
无论如何,最后一点:x86 上的 LOCK 可以应用于 ADD、OR 和 AND,以便为指令获得一致的原子读-修改-写语义。例如,这对于设置标志变量并确保它们不会丢失很重要。没有它,你有这个问题:
线程 1 线程 2 与 [x]、0x1 与 [x]、0x2
在该程序结束时,x 的可能值为 1、2 和 0x1|0x2 (3)。为了获得正确的程序,您需要:
线程 1 线程 2 锁定与 [x]、0x1 锁定与 [x]、0x2
希望这可以帮助。
取决于处理器和线程实现。大多数处理器都有可以原子执行的指令,在这些指令上您可以构建诸如自旋锁之类的东西。例如,IA-32 有一条xchg
指令可以进行原子交换。然后,您可以实现一个简单的自旋锁,例如:
eax = 1;
while( xchg(eax, lock_address) != 0 );
// now I have the lock
... code ...
*lock_address = 0; // release the lock