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

时间:2012-06-18 16:56:33

标签: c++ multithreading boost thread-safety shared-ptr

我想知道,这样实施是否安全? :

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();
}

我们不知道哪个线程会执行实际的realease。 在这样的情况下,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。

3 个答案:

答案 0 :(得分:1)

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

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

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

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

答案 1 :(得分:1)

为了你自己的利益,我会诚实。

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

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
}

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

答案 2 :(得分:-1)

进行编辑:

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

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