Kol*_*nya 3 c++ algorithm mutex atomic mutual-exclusion
我为我的项目制作了一个手工制作的互斥锁,但我怀疑它是否是线程安全的......
bool blocked;
while ( blocked )
{
}
blocked = true;
...
blocked = false;
Run Code Online (Sandbox Code Playgroud)
可以说,线程A传递while循环并且没有及时阻塞标志(没有时间将标志设置为false
),线程B也通过while循环!
可能吗?为什么?
正如我所知,互斥体具有完全相同的工作原理.为什么不能在互斥锁中发生这种情况?我读过有关这不能被中断的原子操作......所以check-if-mutex-available
并mutex-block
不能中断,对不对?
你的代码完全不存在了!
原因是对变量的访问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
Run Code Online (Sandbox Code Playgroud)
(或者,您可以使用std::atomic<bool>
和exchange(true)
,但是atomic_flag
特别为此目的而制作.)
如果这是一个单线程上下文,原子变量不仅会阻止编译器重新排序看起来不相关的代码,而且它们还会使编译器生成必要的代码,以防止CPU本身以允许不一致的方式重新排序指令执行流程.
实际上,如果您想要稍微提高效率,可以在集合和清除操作上要求更便宜的内存排序,如下所示:
while (blocked.test_and_set(std::memory_order_acquire)) { } // lock
// ...
blocked.clear(std::memory_order_release); // unlock
Run Code Online (Sandbox Code Playgroud)
原因是你只关心一个方向的正确排序:另一个方向的延迟更新并不是非常昂贵,但需要顺序一致性(默认情况下)可能很昂贵.
重要提示:上面的代码是一个所谓的自旋锁,因为当状态被锁定时,我们会进行忙转(while
循环).这在几乎所有情况下都非常糟糕.内核提供的互斥系统调用是一个完全不同的鱼群,因为它允许线程向内核发出信号,它可以进入睡眠状态并让内核取消整个线程的计划.这几乎总是更好的行为.