基于std::atomic<bool>的简易线程锁存在何种线程安全隐患?
自旋锁实现的线程安全问题
问题代码
struct MySimpleThreadLock { constexpr MySimpleThreadLock() : bIsLocked(false) {} std::atomic<bool> bIsLocked; void lock() { while (bIsLocked); bIsLocked.store(true); } void unlock() { bIsLocked.store(false); } }; void func() { static MySimpleThreadLock my_lock; my_lock.lock(); // DO SOMETHING my_lock.unlock(); }
问题分析
完全有可能出现你描述的情况。
当前的lock()方法存在致命的线程安全漏洞:自旋等待while (bIsLocked);与后续的bIsLocked.store(true)是两个独立的操作,没有原子性保障。
具体场景:
- 线程1持有锁时,
bIsLocked为true,线程2、3进入自旋循环等待。 - 线程1执行
unlock()将bIsLocked设为false后,线程2和线程3的自旋循环会同时检测到bIsLocked为false,于是都退出循环。 - 紧接着两个线程会先后(甚至在多核CPU下几乎同时)执行
bIsLocked.store(true),最终两个线程都认为自己成功获取了锁,同时进入临界区,导致数据竞争或其他线程安全问题。
修复方案
要解决这个问题,必须把“检查锁状态”和“设置锁为锁定状态”合并为一个原子操作,标准做法是使用std::atomic的compare_exchange_weak或compare_exchange_strong方法:
void lock() { bool expected = false; // 循环尝试原子交换:如果当前锁是未锁定状态(false),则将其设为锁定状态(true) while (!bIsLocked.compare_exchange_weak(expected, true)) { expected = false; // 每次失败后重置expected,因为compare_exchange_weak会更新expected为当前值 } }
compare_exchange_weak是原子操作,它会原子性地完成以下逻辑:
- 比较
bIsLocked的当前值与expected的值。 - 如果相等,将
bIsLocked的值设为true,返回true,表示成功获取锁。 - 如果不等,将
expected更新为bIsLocked的当前值,返回false,继续循环等待。
这样就能确保同一时刻只有一个线程能成功获取锁,避免多线程同时进入临界区的问题。
内容的提问来源于stack exchange,提问作者Zebrafish
相关产品推荐
相关产品推荐

