-2

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

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

我们不知道哪个线程会做实际的释放。boostshared_ptr在这种情况下能安全释放吗?

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

shared_ptr多个线程可以同时“读取”一个实例(仅使用 const 操作访问)。不同 的实例可以由多个线程同时shared_ptr “写入”(使用可变操作,例如 operator=或访问)。reset

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

提前致谢。


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

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

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

4

3 回答 3

1

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

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

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

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

于 2012-06-18T17:14:14.020 回答
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

于 2012-07-26T12:40:25.283 回答
-1

对您的编辑:

为什么不reset()先调用 globalshared_ptr呢?

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