如何使用 boost::shared_ptr 安全地释放线程之间共享的对象

How do I safely release an object shared between threads using boost::shared_ptr?

本文关键字:线程 释放 之间 共享 对象 安全 boost 何使用 shared ptr      更新时间:2023-10-16

我想知道,这样实现安全吗?

typedef shared_ptr<Foo>  FooPtr;    
FooPtr                  *gPtrToFooPtr    // global variable
// init (before any thread has been created)
void init()
{
    gPtrToFooPtr = new FooPtr(new Foo);
}
// thread A, B, C, ..., K
// Once thread Z execute read_and_drop(), 
// no more call to read() from any thread.
// But it is possible even after read_and_drop() has returned,
// some thread is still in read() function.
void read()
{
    FooPtr a = *gPtrToFooPtr;
    // do useful things (read only)
}
// thread Z (executed once)
void read_and_drop()
{
    FooPtr b = *gPtrToFooPtr;
    // do useful things with a (read only)
    b.reset();
}

我们不知道哪个线程会做实际的实现。在这种情况下,boost shared_ptr安全地发布吗?

根据boost的文档,shared_ptr线程的安全性是:

可以"读取"shared_ptr实例(仅使用 const 访问) 操作)同时由多个线程执行。不同 shared_ptr 实例可以"写入"(使用可变操作访问,例如 作为运算符=reset)同时由多个线程。

就我而言,上面的代码没有违反我上面提到的任何线程安全标准。我相信代码应该运行良好。有人告诉我是对还是错吗?

提前谢谢。


已编辑 2012-06-20 01:00 UTC+9

上面的伪代码工作正常。shared_ptr 实现保证在多个线程访问其实例的情况下正常工作(每个线程必须访问其自己的使用复制构造函数实例化shared_ptr实例)。

请注意,在上面的伪代码中,您必须delete gPtrToFooPtrshared_ptr实现最终释放(将引用计数删除一个)它拥有的对象(不是正确的表达式,因为它不是auto_ptr,但谁在乎;))。在这种情况下,您必须意识到它可能会导致多线程应用程序中的 SIGSEGV。

你在这里如何定义"安全"?如果您将其定义为"我想确保对象只销毁一次",那么是的,发布是安全的。但是,问题是在您的示例中,两个线程共享一个智能指针。这根本不安全。一个线程执行的reset()可能对另一个线程不可见。

如文档所述,智能指针提供与内置类型(即指针)相同的保证。因此,在其他线程可能仍在读取时执行不受保护的写入是有问题的。当另一个读取线程将看到另一个读取线程的写入时,它是未定义的。因此,当一个线程调用reset()指针可能不会在另一个线程中重置,因为shared_ptr实例本身是共享的。

如果需要某种线程安全性,则必须使用两个共享指针实例。然后,当然,重置其中一个不会释放对象,因为另一个线程仍然有对它的引用。通常这种行为是有意的。

但是,我认为更大的问题是您滥用了shared_ptrs。使用shared_ptrs指针并在堆上分配shared_ptr(使用 new)是非常罕见的。如果这样做,则会遇到要避免再次使用智能指针的问题(现在必须管理shared_ptr的生命周期)。也许先查看一些关于智能指针及其用法的示例代码。

为了你好,我会说实话。

你的代码做了很多事情,几乎所有的事情都是无用和荒谬的。

typedef shared_ptr<Foo>  FooPtr;    
FooPtr                  *gPtrToFooPtr    // global variable

指向智能指针的原始指针取消了自动资源管理的优势,并且不能解决任何问题。

void read()
{
    FooPtr a = *gPtrToFooPtr;
    // do useful things (read only)
}

a不会以任何有意义的方式使用。

{
    FooPtr b = ...
    b.reset();
}

b.reset()在这里没用,反正b就要被摧毁了。 b在此函数中没有用途。

恐怕你不知道你在做什么,智能指针是干什么的,如何使用shared_ptr,如何做MT编程;所以,你最终会得到一堆荒谬的无用功能来解决问题。

简单地做简单的事情怎么样:

Foo f;
// called before others functions 
void init() {
    // prepare f
}
// called in many threads {R1, R2, ... Rn} in parallel
void read()
{
    // use f (read-only)
}
// called after all threads {R1, R2, ... Rn} have terminated
void read_and_drop()
{
    // reset f
}

可以保证其他线程不读取f之前,不得调用read_and_drop()

要编辑:

为什么不在全球shared_ptr上首先致电reset()

  • 如果您是最后一个访问该对象的人,则将其删除,然后删除堆上的shared_ptr
  • 如果其他线程仍在使用它,则将 ref 计数减少 1,并将全局 ptr 与指向的(仍然存在的)对象"断开连接"。然后,您可以安全地删除堆上的shared_ptr,而不会影响可能仍在使用它的任何线程。