重新创建基于shared_ptr的单例

时间:2015-12-18 17:30:09

标签: c++ multithreading c++11 smart-pointers

内部有一个static shared_ptr<MyClass> get()weak_ptr使用ptr.lock()赠送共享指针。

ptr.lock()给出一个空指针时,应该再次创建单例。

但它是否保证(它没有)前一个单身人士的析构函数已经完成了?可以做些什么呢?

2 个答案:

答案 0 :(得分:1)

  

但它是否保证(它没有)前一个单例的析构函数已经完成?可以做些什么呢?

这是一个不寻常的请求,但我可以看到如果你控制一个外部单例资源可能是必要的。

这是我的解决方案。

请确保在生产中使用它之前仔细检查

#include <memory>
#include <mutex>
#include <condition_variable>

struct tricky_object
{

};


class tricky_cache
{
    struct statics {
        std::mutex _m;
        std::condition_variable _deleted;
        bool _exists = false;
        std::weak_ptr<tricky_object> _cache;
    };

    static statics& get() {
        static statics _;
        return _;
    }

public:
    static
    std::shared_ptr<tricky_object> acquire()
    {
        // get static data
        auto& data = get();

        // lock the cache's mutex
        auto lock = std::unique_lock<std::mutex>(data._m);
        std::shared_ptr<tricky_object> candidate;

        // wait on the condition variable for the following conditions to be true:
        data._deleted.wait(lock, [&data, &candidate] {

            // either the object is in play and we have acquired another reference...
            candidate = data._cache.lock();
            if (candidate)
                return true;

            // ... or (if not) the previous object is actually dead and buried.
            return !data._exists;
        });

        // at this point we still own the lock and wait must have returned true, so...
        // if we own the candidate then it was already in play
        if (candidate)
            return candidate;

        // otherwise the previous object is certainly destroyed and we may create another
        data._cache = candidate = std::shared_ptr<tricky_object>(new tricky_object(),
                                                            [&data](tricky_object*p) {
                                                                // but the custom deleter needs some trickery
                                                                delete p;
                                                                if (p) {
                                                                    auto lock = std::unique_lock<std::mutex>(data._m);
                                                                    data._exists = false;
                                                                    lock.unlock();
                                                                    data._deleted.notify_all();
                                                                }
                                                            });
        // and we should record the fact that the object now exists...
        data._exists = true;
        lock.unlock();
        // ... and inform all waiters that they may continue acquiring
        data._deleted.notify_all();
        return candidate;
    }
};


int main()
{
    auto p = tricky_cache::acquire();

    return 0;
}

答案 1 :(得分:0)

可以做些什么?停止使用可怕的编程风格。

如果您要使用单身,那么它应该是单身:一个实例,句号。没有必要用智能指针管理它的生命周期;它永远存在。什么是摧毁它只是为了以后再创造它?特别是如果娱乐功能没有给出特殊参数以便以后重新创建它?

然而,问题是:

  

是否保证(它没有)前一个单身人士的析构函数已经完成

重要吗?为了使对象的析构函数启动,对象的shared_ptr引用计数必须为零。所以weak_ptr已经是空的。对象的生命周期在其析构函数启动时结束(正如对象的生命周期在其构造函数完成时开始)。因此,单身人士本身已经被摧毁;你只是做清理工作。

因此,在旧实例的析构函数的callstack中创建单例的新实例没有问题。它根本不会自己进入。

线程

在多线程环境中,在get函数中没有某种锁定返回/创建单例的情况下,这种接口已经非常破坏。如果没有这种互斥,多个线程可能会尝试同时创建单例,这可能导致构造多个单例实例。

至于单身人士本身的资源,这些资源的释放必须由某种形式的互斥机制来管理。资源本身单身的唯一时间。但与我们讨论的单身人士不同,它不会被多段代码所拥有。

在这种情况下,您的单身人士根本不应拥有该资源的所有权。它可以引用它,但它不能破坏或创建它。