std :: shared_ptr线程安全解释

Goo*_*ofy 100 c++ shared-ptr c++11

我正在阅读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));
Run Code Online (Sandbox Code Playgroud)

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

Nic*_*rca 85

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

但是你编辑的结尾部分

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

是不正确的.只有d将指向新A(10),和a,b以及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;                                                                                                          
}
Run Code Online (Sandbox Code Playgroud)

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

这段代码的输出是

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

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


Not*_*ore 34

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

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

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

编辑:稍微跟进,如果你想了解共享指针如何工作,你可能想看看boost::shared_ptr源:http://www.boost.org/doc/libs/1_37_0/boost/shared_ptr.hpp.

  • 1.当你说"'shared_ptrs'使用引用计数值的原子增量/减量时." 你的意思是他们不使用任何内部锁来进行原子递增/递减,这会进行上下文切换吗?在简单的语言中,多个线程可以在不使用锁的情况下递增/递减引用计数吗?原子增量是通过特殊的atomic_test_and_swap/atomic_test_and_increment指令完成的吗? (3认同)
  • 是.根据我的理解,标准并没有说它必须是无锁的.但在最新的GCC和MSVC中,它在Intel x86硬件上是无锁的,我认为其他优秀的编译器可能会在硬件支持时也这样做. (2认同)

Lot*_*har 16

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

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

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

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

  • 这个答案非常具有误导性,而且根本不清楚。是的,单个 std::shared_ptr 实例不是线程安全的,但这不是预期的用例*也不是问题中所要求的。 (7认同)
  • 这可以措辞更好。当总是跨线程边界**按值**(复制/移动)使用时,`std::shared_ptr&lt;T&gt;`实例保证线程安全。所有其他用途,“std::shared_ptr&lt;T&gt;&amp;”跨线程边界都是不安全的 (6认同)
  • 这不是所要求的,因为除了 init 之外,OP 不会跨线程共享 share_ptr 对象。OP中的每个线程都有它自己的shared_ptr对象,并且所有问题AFAICT都与这些每线程shared_ptr对象(指向共享对象)有关。 (3认同)
  • 我们可以说单个`std :: shared_ptr`实例不是线程安全的。来自std :: shared_ptr参考:`如果多个执行线程在不同步的情况下访问同一个shared_ptr,并且这些访问中的任何一个都使用了shared_ptr的非常量成员函数,则将发生数据争用; (2认同)