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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 15:35:29