: share_ptr 线程安全解释

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

  1. 标准保证引用计数处理是线程安全的,并且与平台无关,对吗?
  2. 类似的问题-标准保证只有一个线程(持有最后引用)将调用共享对象上的 delete,对吗?
  3. Share _ 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 中调用复位()会删除在第一个线程中创建的 A 类的前一个实例并用新实例替换它吗?而且在 IV 线程中调用复位()之后,其他线程将只看到新创建的对象?

88168 次浏览
  1. 正确,shared_ptr使用引用计数值的原子增量/减量。

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

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

编辑: 稍微跟进一下,如果你想了解共享指针一般是如何工作的,你可能想看看 boost::shared_ptr源代码: https://www.boost.org/doc/libs/release/boost/smart_ptr/shared_ptr.hpp

正如其他人指出的那样,关于你最初的3个问题,你已经正确地解决了。

但是你编辑的结尾部分

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

是不正确的。只有 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

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

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

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

如果在另一个线程使用 std::shared_ptr时将一个新对象分配给它,那么它最终可能会得到一个新对象指针,但仍然使用一个指向旧对象控制块的指针 = > CRASH。