C++ 如何使用boost::shared\u ptr安全地释放线程之间共享的对象?

C++ 如何使用boost::shared\u ptr安全地释放线程之间共享的对象?,c++,multithreading,boost,thread-safety,shared-ptr,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 // On

我想知道,这样实施安全吗

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();
}
typedef共享\u ptr FooPtr;
FooPtr*gPtrToFooPtr//全局变量
//init(在创建任何线程之前)
void init()
{
gptrtofootr=新FooPtr(新Foo);
}
//螺纹A、B、C、…、K
//一旦线程Z执行read_和_drop(),
//不再从任何线程调用read()。
//但即使在read_和_drop()返回后也有可能,
//某些线程仍在read()函数中。
无效读取()
{
FooPtr a=*gPtrToFooPtr;
//做有用的事情(只读)
}
//线程Z(执行一次)
无效读取和删除()
{
FooPtr b=*gPtrToFooPtr;
//使用(只读)命令执行有用的操作
b、 重置();
}
我们不知道哪个线程将执行实际的realess。 boost的
shared\u ptr
在这种情况下发布安全吗

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

shared_ptr
实例可以“读取”(仅使用常量访问) 操作)由多个线程同时执行不同
共享
实例可以“写入”(使用可变操作访问),例如 as操作员通过多个线程同时执行
=
重置

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

提前谢谢


编辑日期2012-06-20 01:00 UTC+9 上面的伪代码运行良好。
shared_ptr
实现保证在多个线程正在访问其实例的情况下正常工作(每个线程必须访问其自己的
shared_ptr
实例,该实例是使用复制构造函数实例化的)


注意,在上面的伪代码中,您必须
删除gPtrToFooPtr
,才能让
共享ptr
实现最终释放(将引用计数减少一)它所拥有的对象(不是正确的表达式,因为它不是
自动ptr
,但谁在乎呢;)。在这种情况下,您必须意识到,它可能会在多线程应用程序中导致SIGSEGV。

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

如文档所述,智能指针提供与内置类型(即指针)相同的保证。因此,在另一个线程可能仍在读取时执行无防护写入是有问题的。当另一个读取线程看到另一个线程的写入时,它是未定义的。因此,当一个线程调用
reset()

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

然而,我认为更大的问题是您滥用了共享资源。使用共享\u ptr的指针并在堆上分配共享\u ptr(使用new)是非常少见的。如果您这样做,您就会遇到希望避免再次使用智能指针的问题(您现在必须管理共享\u ptr的生命周期)。也许可以先查看一些有关智能指针及其用法的示例代码。

要编辑:

为什么不先在全局
共享\u ptr
上调用
reset()

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

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

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

      typedef shared_ptr<Foo>  FooPtr;    
      FooPtr                  *gPtrToFooPtr    // global variable
      
      a
      没有以任何有意义的方式使用

      {
          FooPtr b = ...
          b.reset();
      }
      
      b.reset()
      在这里是无用的,
      b
      无论如何都将被销毁<代码>b
在此功能中没有任何用途

我恐怕你不知道你在做什么,智能指针的用途是什么,如何使用
shared\u ptr
,以及如何进行机器翻译编程;因此,你最终会得到这堆荒谬的无用功能,而无法解决问题

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

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

那么,将全局共享的类型更改为,比如说,
FooPtr gFooPtr和,分配给
FooPtr a=gFooPtr
FooPtr b=gFooPtr?我猜这与使用指向动态分配的共享\u ptr实例的指针相同。我认为,共享\u ptr的引用计数将阻止“无防护写入”,该引用计数由boost提供的无锁机制保护。因为只有在释放Foo实例时才可能进行写入,并且它受shared_ptr的引用计数保护。这样更好(请参阅我文章中的编辑,最好再阅读一遍)。但是,对象有两个引用,因此,仅重置其中一个引用不会释放对象。您必须在每个线程中重置指针。这通常是智能指针的预期行为:如果一个线程仍然有
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
}