【问题标题】:Thread safety with std::shared_ptrstd::shared_ptr 的线程安全
【发布时间】:2019-06-07 20:35:00
【问题描述】:

我正在阅读有关 std::shared_ptr 的线程安全以及它提供的原子操作重载的信息,并且想知道它在类中的特定用例。

根据我对 shared_ptr 所承诺的线程安全性的理解,拥有这样的 get 方法是安全的:

class MyClass 
{
std::shared_ptr<int> _obj;
public:
    void start() 
    {
        std::lock_guard<std::mutex> lock(_mtx);
        _obj = std::make_shared<int>(1);
    }

    void stop()
    {
          std::lock_guard<std::mutex> lock(_mtx);
        _obj.reset();

    }
    std::shared_ptr<int> get_obj() const
    {
        return _obj; //Safe (?)
    }
};

getter 应该是安全的,因为对象将在任何线程的任何时候被初始化或为空。

但是如果我想在对象为空的情况下抛出异常,我需要在返回它之前检查它,我现在是否必须在那里加锁(因为可能会在 if 和 return 之间调用 stop() )?或者是否可以使用共享指针的锁定机制而不使用该方法中的锁定:

   std::shared_ptr<int> get_obj() const
    {
        auto tmp = _obj;
        if(!tmp) throw std::exception();
        return tmp;
    }

【问题讨论】:

  • get_objstartstop 竞争。还需要保证前者不会与后者同时被调用。第二个版本与第一个版本具有相同的数据竞争 - 没有更好也没有更差。
  • 另见stackoverflow.com/questions/20705304/…。不是完全重复,但答案与您的问题相关。

标签: c++ c++11 thread-safety shared-ptr race-condition


【解决方案1】:

std::shared_ptr 实例不是线程安全的。可以从多个线程修改都指向同一个对象的多个实例,但单个实例不是线程安全的。见https://en.cppreference.com/w/cpp/memory/shared_ptr

所有成员函数(包括复制构造函数和复制赋值)都可以由多个线程在 shared_ptr 的不同实例上调用而无需额外同步,即使这些实例是副本并共享同一对象的所有权。如果多个执行线程在没有同步的情况下访问同一个 shared_ptr 并且这些访问中的任何一个使用 shared_ptr 的非常量成员函数,则将发生数据竞争;原子函数的 shared_ptr 重载可用于防止数据竞争。

因此,您要么需要在 get_obj 方法中锁定互斥体,要么在 startstop 方法中使用 std::atomic_loadstd::atomic_store

【讨论】:

    猜你喜欢
    • 2013-01-07
    • 1970-01-01
    • 1970-01-01
    • 2013-05-05
    • 1970-01-01
    • 1970-01-01
    • 2021-12-19
    相关资源
    最近更新 更多