std :: shared_ptr线程安全解释

时间:2012-02-03 11:19:02

标签: c++ c++11 shared-ptr

我正在阅读http://gcc.gnu.org/onlinedocs/libstdc++/manual/shared_ptr.html并且一些线程安全问题对我来说仍然不明确:

  1. 标准保证引用计数是处理线程安全的并且它与平台无关,对吧?
  2. 类似的问题 - 标准保证只有一个线程(持有最后一个引用)会在共享对象上调用delete,对吗?
  3. shared_ptr不保证存储在其中的对象的线程安全吗?
  4. 编辑:

    伪代码:

    // Thread I
    shared_ptr<A> a (new A (1));
    
    // Thread II
    shared_ptr<A> b (a);
    
    // Thread III
    shared_ptr<A> c (a);
    
    // Thread IV
    shared_ptr<A> d (a);
    
    d.reset (new A (10));
    

    在线程IV中调用reset()将删除在第一个线程中创建的A类的先前实例并将其替换为新实例?此外,在IV线程中调用reset()之后,其他线程只会看到新创建的对象吗?

3 个答案:

答案 0 :(得分:81)

正如其他人所指出的那样,你已经弄清楚了原来的3个问题。

但是编辑的结尾部分

  

在线程IV中调用reset()将删除在第一个线程中创建的A类的先前实例并将其替换为新实例?此外,在IV线程中调用reset()之后,其他线程只会看到新创建的对象吗?

不正确。只有d会指向新的A(10),而abc将继续指向原始A(1)。在以下简短的例子中可以清楚地看到这一点。

#include <memory>
#include <iostream>
using namespace std;

struct A
{
  int a;
  A(int a) : a(a) {}
};

int main(int argc, char **argv)
{
  shared_ptr<A> a(new A(1));
  shared_ptr<A> b(a), c(a), d(a);

  cout << "a: " << a->a << "\tb: " << b->a
     << "\tc: " << c->a << "\td: " << d->a << endl;

  d.reset(new A(10));

  cout << "a: " << a->a << "\tb: " << b->a
     << "\tc: " << c->a << "\td: " << d->a << endl;

  return 0;                                                                                                          
}

(显然,我没有打扰任何线程:这不会影响shared_ptr::reset()行为。)

此代码的输出是

  

a:1 b:1 c:1 d:1

     

a:1 b:1 c:1 d:10

答案 1 :(得分:33)

  1. 正确,shared_ptr使用引用计数值的原子递增/递减。

  2. 标准保证只有一个线程会在共享对象上调用delete运算符。我不确定它是否具体指定删除其共享指针副本的最后一个线程将是调用delete的那个(实际上可能就是这种情况)。

  3. 否则不会,多个线程可以同时编辑存储在其中的对象。

  4. 编辑:稍加跟进,如果您想了解共享指针的工作原理,您可能需要查看boost::shared_ptr来源:http://www.boost.org/doc/libs/1_37_0/boost/shared_ptr.hpp

答案 2 :(得分:13)

std::shared_ptr不是线程安全的。

共享指针是一对两个指针,一个指向对象,一个指向一个控制块(持有ref计数器,链接到弱指针......)。

可以有多个std :: shared_ptr,每当他们访问控制块来更改引用计数器时,它都是线程安全的,但std::shared_ptr本身不是线程安全的或原子的。

如果在另一个线程使用它时将新对象分配给std::shared_ptr,它可能会以新对象指针结束,但仍然使用指向旧对象的控制块的指针=&gt; CRASH。