std :: shared_ptr线程安全说明


106

我正在阅读http://gcc.gnu.org/onlinedocs/libstdc++/manual/shared_ptr.html,但是对于我来说,一些线程安全性问题仍然不清楚:

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

编辑:

伪代码:

// 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()之后,其他线程将仅看到新创建的对象吗?


24
对,对,对。
spraff

16
你应该使用make_shared,而不是new
QDII

Answers:


87

正如其他人指出的那样,您已经对最初的3个问题正确找到了答案。

但是您编辑的结尾部分

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

是不正确的。只有d将指向新A(10),和ab以及c将继续指向原始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


35
  1. 正确的是,shared_ptr使用参考计数值的原子增量/减量。

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

  3. 不,他们没有,存储在其中的对象可以由多个线程同时进行编辑。

编辑:轻微的后续操作,如果您想大致了解共享指针的工作方式,则可能需要查看boost::shared_ptr源代码:http : //www.boost.org/doc/libs/1_37_0/boost/shared_ptr.hpp


3
1.当您说“'shared_ptrs'使用参考计数值的原子增量/减量”时。您是说他们不对原子增量/减量使用任何内部锁,上下文会切换哪个?用简单的语言,多个线程是否可以在不使用锁定的情况下增加/减少引用计数?原子增量是通过特殊的atomic_test_and_swap / atomic_test_and_increment指令完成的吗?
rahul.deshmukhpatil 2015年

@rahul编译器可以自由使用互斥锁/锁定,但是大多数优秀的编译器在可以无锁定的平台上不会使用互斥锁/锁定。
伯纳德

@Bernard:您的意思是它取决于平台的“ compilers std lib shared_ptr”实现吗?
rahul.deshmukhpatil'2

2
是。根据我的理解,该标准并没有说它必须是无锁的。但是在最新的GCC和MSVC中,它在Intel x86硬件上是无锁的,并且我认为如果硬件支持,其他好的编译器也可能会这样做。
伯纳德

18

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

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

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

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


4
我们可以说单个std::shared_ptr实例不是线程安全的。来自std :: shared_ptr参考:If multiple threads of execution access the same shared_ptr without synchronization and any of those accesses uses a non-const member function of shared_ptr then a data race will occur;
JKovalsky

可以用更好的措辞。阿std::shared_ptr<T>时总是用实例是保证线程安全由值跨越线程边界(复制/移动)。所有其他用途std::shared_ptr<T>&在线程边界上都是不安全的
WhiZTiM
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.