C++如何仅在变量为特定值时锁互斥锁?原子事务实现方案咨询
问题:如何在特定条件下锁定互斥锁,实现原子事务?
现有场景如下,类M的公有方法f1、f2各自持有互斥锁,但需要在f()中原子调用这两个方法,避免执行过程中被其他线程打断:
class M { std::mutex m; public: void f1() { std::lock_guard l(m); // do stuff... } void f2() { std::lock_guard l(m); // do stuff... } void f() { // 需要原子执行f1和f2的逻辑,直接调用会重复加锁导致问题 // mutex.lock(); f1(); f2(); // mutex.unlock(); }
用户希望避免使用recursive_mutex,也不想为每个公有方法编写冗余的无锁版本,同时对unique_lock的条件创建写法不满意,询问C++20是否有更优特性或其他可行思路。
可行解决方案
1. 核心逻辑抽离+私有无锁实现
这是最简洁且易维护的方案:将f1、f2的业务逻辑抽离到私有无锁方法中,公有方法统一负责加锁逻辑,f()直接调用无锁方法实现原子事务。
#include <mutex> class M { std::mutex m_; // 私有无锁核心实现 void f1_impl() { /* do stuff... */ } void f2_impl() { /* do stuff... */ } public: void f1() { std::lock_guard<std::mutex> l(m_); f1_impl(); } void f2() { std::lock_guard<std::mutex> l(m_); f2_impl(); } void f() { std::lock_guard<std::mutex> l(m_); // 原子执行两个核心逻辑,无需重复加锁 f1_impl(); f2_impl(); } };
这种方式既避免了递归锁,又最小化了代码冗余,仅需编写一次核心逻辑。
2. C++20结合std::unique_lock延迟锁定
利用std::unique_lock的defer_lock特性延迟加锁,配合锁状态标记实现条件加锁:
#include <mutex> class M { std::mutex m_; bool is_locked_ = false; void f1_impl() { /* do stuff... */ } void f2_impl() { /* do stuff... */ } public: void f1(bool already_locked = false) { std::unique_lock<std::mutex> l(m_, std::defer_lock); if (!already_locked) { l.lock(); is_locked_ = true; } f1_impl(); if (!already_locked) { is_locked_ = false; } } void f() { std::lock_guard<std::mutex> l(m_); is_locked_ = true; f1(true); f2(true); is_locked_ = false; } };
通过传递already_locked参数,让方法自行判断是否需要加锁,既符合RAII原则,又灵活支持原子调用场景。
3. 用原子标记跟踪锁状态
如果需要更线程安全的状态跟踪,可以使用C++11起的std::atomic_flag替代普通bool标记:
#include <mutex> #include <atomic> class M { std::mutex m_; std::atomic_flag lock_flag_ = ATOMIC_FLAG_INIT; void f1_impl() { /* do stuff... */ } void f2_impl() { /* do stuff... */ } public: void f1() { std::lock_guard<std::mutex> l(m_); lock_flag_.test_and_set(); f1_impl(); lock_flag_.clear(); } void f() { std::lock_guard<std::mutex> l(m_); lock_flag_.test_and_set(); f1_impl(); f2_impl(); lock_flag_.clear(); } };
原子标记确保锁状态的修改是线程安全的,适合高并发场景下的状态检查。
4. 模板函数封装锁逻辑
通过模板函数封装锁的判断逻辑,进一步减少重复代码:
#include <mutex> #include <functional> class M { std::mutex m_; void f1_impl() { /* do stuff... */ } void f2_impl() { /* do stuff... */ } // 通用锁逻辑封装 template<typename Func> void execute_with_lock(bool already_locked, Func&& func) { if (already_locked) { func(); } else { std::lock_guard<std::mutex> l(m_); func(); } } public: void f1(bool already_locked = false) { execute_with_lock(already_locked, [this](){ f1_impl(); }); } void f2(bool already_locked = false) { execute_with_lock(already_locked, [this](){ f2_impl(); }); } void f() { std::lock_guard<std::mutex> l(m_); f1(true); f2(true); } };
这种方式将锁逻辑统一管理,后续新增方法时无需重复编写加锁判断,扩展性更强。
内容的提问来源于stack exchange,提问作者Raffaello
相关产品推荐
相关产品推荐

