3

这是一个锁,一次只能由一个执行线程持有。另一个执行线程尝试获取锁会导致后一个循环,直到锁被释放。

当两个线程试图同时获取锁时,它是如何处理

我认为这个问题也适用于其他各种互斥体实现。

4

2 回答 2

7

正如上一张海报所指出的,每一种现代机器类型都有一种特殊的指令类别,称为“原子”,它们确实像上一张海报所表明的那样运行……它们至少针对指定的内存位置序列化执行。

在 x86 上,有一个 LOCK 汇编器前缀,指示机器应该以原子方式处理下一条指令。当遇到指令时,x86 上会发生几件事情。

  1. 挂起的读取预取被取消(这意味着 CPU 不会向程序提供可能在原子范围内变得陈旧的数据)。
  2. 对内存的挂起写入被刷新。
  3. 该操作以原子方式执行、保证并针对其他 CPU 进行序列化。在这种情况下,“序列化”意味着“它们一次发生一个”。原子的意思是“该指令的所有部分都在没有其他任何干预的情况下发生”。

对于 x86,有两个常用的指令用于实现锁。

  1. CMPXCHG。有条件的交换。伪代码:
uint32 cmpxchg(uint32 *memory_location,uint32 old_value,uint32 new_value){
    原子地{
        if (*memory_location == old_value)
            *memory_location = new_value;
        返回旧值;
    }
}
  1. XCHG. 伪代码:
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

希望这可以帮助。

于 2010-05-24T23:01:26.723 回答
2

取决于处理器和线程实现。大多数处理器都有可以原子执行的指令,在这些指令上您可以构建诸如自旋锁之类的东西。例如,IA-32 有一条xchg指令可以进行原子交换。然后,您可以实现一个简单的自旋锁,例如:

  eax = 1;
  while( xchg(eax, lock_address) != 0 );
  // now I have the lock
  ... code ...
  *lock_address = 0; // release the lock
于 2010-05-23T13:50:16.843 回答