对于多线程,shared_ptr销毁是否安全?

Is shared_ptr destruction safe with multiple threads?

本文关键字:是否 安全 ptr 多线程 shared      更新时间:2023-10-16

我有两个类似的类:

class Foo {
 public:
  void bar() {
    std::lock_guard<std::mutex> lock(m_mutex);
    m_data.push_back('x');
  }
 private:
  std::string m_data;
  std::mutex m_mutex;
};
class Pool {
 public:
   static std::shared_ptr<Foo> Create(int index) {
     std::lock_guard<std::mutex> lock(m_mutex);
     if (m_pool.size() > 10) {
       m_pool.erase(m_pool.begin());
     }
     std::shared_ptr<Foo>& ptr = m_pool[index];
     if (!ptr) ptr.reset(new Foo);
     return ptr;
   }
 private:
   static std::mutex m_mutex;
   static std::map<int, std::shared_ptr<Foo>> m_pool;
};

和几个运行此代码的线程:

void parallel_function(int index) {
  // several threads can get the same index
  std::shared_ptr<Foo> foo = Pool::Create(index);
  foo->bar();
}

Cppreference说

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

两个问题:

  1. 由于Pool::Create总是返回shared_ptr的副本,我假设每个shared_ptr的复制和销毁是线程安全的,如果它发生在m_pool.eraseparallel_function的末尾。这是正确的吗?

  2. 我调用shared_ptr::operator->,这是一个const成员函数,函数Foo::bar是线程安全的。这里是否存在数据竞赛?

总结一下我的评论。

  1. 是的,这是线程安全的,因为你在不同的线程中操作单独的shared_ptr副本。这是传递shared_ptr s副本的少数情况之一。
  2. operator->是const成员。所以基本上你的代码是好的,只要Foo::bar是无种族的站真(它现在显然是)。