You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C++中用原子变量实现互斥锁?解决银行账户取款线程同步问题

解决原子变量取款时的余额负数问题 & 用原子变量实现互斥锁

嘿,我完全懂你遇到的这个坑——你现在的取款逻辑里,余额检查和后续的原子更新不是一个原子操作,这就给了竞态条件可乘之机:两个线程都先读到了300的余额,都觉得够取200,然后先后执行compare_exchange,第二个线程就会把已经变成100的余额再减200,直接搞成负数。

下面给你两种解决方案,一种是更高效的原子操作优化方案(不用锁,从根源上解决竞态),另一种是用原子变量实现自旋锁(满足你“用原子变量当互斥锁”的需求):

方案一:把余额检查+更新合并为原子循环操作

这是最优解,不需要额外的锁,直接利用compare_exchange的循环特性,把余额检查和更新绑定在一个原子操作的重试逻辑里:

void withdraw(int amount) {
    float old_balance;
    do {
        old_balance = m_iBalance.load();
        // 第一步:先检查当前余额是否足够,不够直接退出
        if (old_balance < amount) {
            std::cout << "余额不足,无法取出 " << amount << std::endl;
            return;
        }
        // 第二步:尝试原子更新,只有当当前余额和old_balance一致时才会成功
        // 如果失败,说明其他线程已经修改了余额,old_balance会被自动更新为最新值,进入下一轮循环
    } while (!m_iBalance.compare_exchange_weak(old_balance, old_balance - amount));
    
    // 走到这里说明取款成功,余额肯定是非负的
}

为什么这个能解决问题?

每次循环都会重新读取最新的余额,然后检查是否足够。如果在检查之后、更新之前,其他线程修改了余额,compare_exchange_weak会失败,同时自动把old_balance更新为当前的最新余额,下一轮循环就会用新的余额重新检查,不会再出现“两个线程都基于旧余额执行减法”的情况。

方案二:用原子变量实现自旋锁(模拟互斥锁)

如果你确实需要用原子变量来做互斥,让同一时间只有一个线程进入取款函数,可以用一个原子布尔变量实现自旋锁:

#include <atomic>
#include <iostream>
#include <thread> // 用于std::this_thread::yield()

std::atomic<bool> g_withdraw_lock = false; // 锁标志,false表示未锁定
std::atomic<float> m_iBalance;

void withdraw(int amount) {
    // 自旋等待获取锁:不断尝试把锁从false改成true,成功则获取到锁
    while (!g_withdraw_lock.compare_exchange_weak(false, true)) {
        // 可以加yield()让出CPU,减少空循环的资源浪费
        std::this_thread::yield();
    }

    // 临界区:这里同一时间只有一个线程能进入
    const float current_balance = m_iBalance.load();
    if (current_balance >= amount) {
        m_iBalance.store(current_balance - amount);
    } else {
        std::cout << "余额不足,无法取出 " << amount << std::endl;
    }

    // 释放锁:把标志改回false
    g_withdraw_lock.store(false);
}

注意事项

  • 自旋锁在高并发场景下可能会浪费CPU资源(线程空循环等待),如果是单核心CPU,不如直接用std::mutex更高效;但多核心场景下,自旋锁的开销比互斥锁小。
  • 一定要确保锁被正确释放,比如如果取款函数里有异常,要记得用RAII封装锁(比如自己写个简单的LockGuard类),避免死锁。

总结

优先推荐方案一,因为它更高效,也更符合原子变量的设计初衷;方案二是满足你“用原子变量当互斥锁”需求的实现方式,但要注意自旋锁的适用场景。

内容的提问来源于stack exchange,提问作者Ettur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 15:32:44