C++线程安全Accessor与Mutator疑问:是否安全及如何优化
线程安全疑问:Accessor与Mutator的多线程风险及优化方案
我有如下类似的架构代码:
struct Object { Object(int& n) : value{n} {} int& value; }; struct Accessor { Accessor(const int& o) : o_(o) {} const int& getValue() { return o_; } private: const int& o_; }; struct Mutator { Mutator(int& o) : o_(o) {} int& getValue() { return o_; } private: int& o_; }; struct ObjectA : Object { shared_ptr<const Accessor> getAcc() { return make_shared<Accessor>(Accessor{value}); } shared_ptr<Mutator> getMut() { return make_shared<Mutator>(Mutator{value}); } };
我的目标是通过Mutator写入对象值,通过Accessor读取对象值。单元测试运行全部正常,但我担心在多线程环境下会存在问题,想请教该Accessor和Mutator是否线程安全?若不安全,该如何优化使其具备线程安全性?
当前实现的线程安全问题
当前代码完全不具备线程安全性,核心原因如下:
- 共享的
int& value没有任何同步机制:多线程同时通过Mutator写入、Accessor读取时,会触发数据竞争,导致读取到脏数据;在部分CPU架构下,非原子的int读写可能被拆分为多个指令,直接引发未定义行为。 - Accessor和Mutator仅简单持有变量引用,未对读写操作做任何同步控制,完全依赖外部调用者的行为,在多线程场景下不可靠。
优化方案
方案1:互斥锁实现读写同步
给Object添加互斥锁,让Accessor和Mutator在操作时持有锁,确保同一时间只有一个线程能访问共享变量:
#include <mutex> #include <memory> struct Object { Object(int& n) : value{n} {} int value; std::mutex mtx; // 新增互斥锁 }; struct Accessor { Accessor(const Object& obj) : obj_(obj) {} int getValue() { std::lock_guard<std::mutex> lock(obj_.mtx); // 读取时加锁 return obj_.value; } private: const Object& obj_; // 改为持有Object引用,统一锁控制 }; struct Mutator { Mutator(Object& obj) : obj_(obj) {} // 建议新增setValue方法,避免直接返回引用暴露内部变量 void setValue(int new_val) { std::lock_guard<std::mutex> lock(obj_.mtx); // 写入时加锁 obj_.value = new_val; } private: Object& obj_; // 改为持有Object引用 }; struct ObjectA : Object { using Object::Object; std::shared_ptr<const Accessor> getAcc() { return std::make_shared<Accessor>(*this); } std::shared_ptr<Mutator> getMut() { return std::make_shared<Mutator>(*this); } };
说明:
std::lock_guard会自动管理锁的生命周期,避免忘记解锁导致死锁。- 避免直接返回内部变量引用:防止调用者拿到引用后在锁作用域外操作,破坏线程安全。
方案2:原子变量(适合简单基础类型)
如果仅对int类型做读写操作,可以直接用std::atomic<int>替代普通int,无需显式锁:
#include <atomic> #include <memory> struct Object { Object(int n) : value{n} {} // 原子类型不能绑定普通int引用,改为值传递 std::atomic<int> value; }; struct Accessor { Accessor(const Object& obj) : obj_(obj) {} int getValue() { return obj_.value.load(std::memory_order_acquire); // 原子读取,保证内存可见性 } private: const Object& obj_; }; struct Mutator { Mutator(Object& obj) : obj_(obj) {} void setValue(int new_val) { obj_.value.store(new_val, std::memory_order_release); // 原子写入,保证内存可见性 } private: Object& obj_; }; struct ObjectA : Object { using Object::Object; std::shared_ptr<const Accessor> getAcc() { return std::make_shared<Accessor>(*this); } std::shared_ptr<Mutator> getMut() { return std::make_shared<Mutator>(*this); } };
说明:
std::atomic保证读写操作的原子性,避免数据竞争。- 内存序
acquire和release确保读取操作能看到之前所有已完成的写入操作,解决多线程下的内存可见性问题。
内容的提问来源于stack exchange,提问作者LunaticJape
相关产品推荐
相关产品推荐

