你的代码完全失效了!
原因是对变量blocked
的访问不是原子的。如果两次读取发生在第一个线程写出true
更新并且更新传播到所有 CPU 之前,两个线程可以同时读取它并确定互斥锁已解锁。
你需要原子变量和原子交换来解决这个问题。该atomic_flag
类型正是您想要的:
#include <atomic>
std::atomic_flag blocked;
while (blocked.test_and_set()) { } // spin while "true"
// critical work goes here
blocked.clear(); // unlock
(或者,您可以使用std::atomic<bool>
and exchange(true)
,但 theatomic_flag
是专门为此目的而制作的。)
如果这是一个单线程上下文,原子变量不仅可以防止编译器重新排序看起来不相关的代码,而且它们还可以使编译器生成必要的代码,以防止 CPU 本身以允许不一致的方式重新排序指令执行流程。
事实上,如果你想稍微提高一点效率,你可以在 set 和 clear 操作上要求更便宜的内存排序,如下所示:
while (blocked.test_and_set(std::memory_order_acquire)) { } // lock
// ...
blocked.clear(std::memory_order_release); // unlock
原因是您只关心一个方向上的正确排序:另一个方向上的延迟更新并不是很昂贵,但要求顺序一致性(默认情况下)可能很昂贵。
重要提示:上面的代码是所谓的自旋锁,因为当状态被锁定时,我们会进行一次忙自旋(while
循环)。这在几乎所有情况下都非常糟糕。内核提供的互斥体系统调用是完全不同的鱼,因为它允许线程向内核发出信号,它可以进入睡眠状态并让内核重新调度整个线程。这几乎总是更好的行为。