std::shared_ptr 在多大程度上保证了线程安全?

问题描述 投票:0回答:3

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

  1. 标准保证引用计数的处理是线程安全的并且是独立于平台的,对吗?
  2. 类似的问题 - 标准保证只有一个线程(保存最后一个引用)会调用共享对象上的删除,对吗?
  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() 之后,其他线程将只能看到新创建的对象?

c++ c++11 thread-safety shared-ptr smart-pointers
3个回答
111
投票

正如其他人所指出的,你已经正确地解决了你原来的 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;                                                                                                          
}

(显然,我没有费心任何线程:这不会影响

shared_ptr::reset()
行为。)

这段代码的输出是

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

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


47
投票
  1. 正确,

    shared_ptr
    使用引用计数值的原子增量/减量。

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

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

编辑:稍微跟进一下,如果您想了解共享指针一般如何工作,您可能需要查看

boost::shared_ptr
源:https://www.boost.org/doc/libs/release/boost /smart_ptr/shared_ptr.hpp.


34
投票

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

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

可以有多个 std::shared_ptr ,每当它们访问控制块以更改引用计数器时,它都是线程安全的,但

std::shared_ptr
本身不是线程安全或原子的。

如果您将一个新对象分配给

std::shared_ptr
而另一个线程使用它,它可能最终会得到新对象指针,但仍然使用指向旧对象控制块的指针=>崩溃。

© www.soinside.com 2019 - 2024. All rights reserved.