具有本地静态变量的Singleton构造函数

时间:2017-04-13 01:47:36

标签: c++ multithreading c++11 singleton

我正在使用Singleton设计模式进行练习。从讨论herehere,我理解本地静态变量的初始化是自C ++ 11以来的线程安全。请考虑下面的代码段,

std::shared_ptr<ApiFacade> ApiFacade::GetInstance(const std::string & url)
{
    // Initialization of function-local statics is guaranteed to occur only
    // once even when called from multiple threads, and may be more efficient
    // than the equivalent code using std::call_once.
    static std::shared_ptr<ApiFacade> rest = nullptr;
    if (rest == nullptr)
    {
        if (!url.empty())
        {
            rest = std::shared_ptr<ApiFacade>(new ApiFacade(url));
        }
        else
        {
            throw std::invalid_argument("Failed creating REST API object, Pass a valid URL");
        }
    }

    return rest;
}

此处首先使用nullptr初始化本地静态,然后使用rest = std::shared_ptr<ApiFacade>(new ApiFacade(url));为其指定适当的指针。我想知道在为本地static分配ApiFacade实例之前是否确保线程安全,或者在这种情况下我是否还需要使用DCLP。这里使用static进行本地nullptr初始化,稍后使用ApiFacade的实例进行初始化。

但是我尝试解决竞争条件如下,但@Etherealone解决方案看起来不错

{
    static std::shared_ptr<ApiFacade> rest = nullptr;

    if (rest == nullptr)
    {
        // Mutex to provide exclusive access to resource.
        std::recursive_mutex singleTonMtx = {};
        std::lock_guard<std::recursive_mutex> lock(singleTonMtx);
        if (!url.empty() && (rest == nullptr))
        {
            rest = std::shared_ptr<ApiFacade>(new ApiFacade(url));
        }
        else
        {
            throw std::invalid_argument("Failed creating API object, Pass a valid URL");
        }
    }

    return rest;
}

1 个答案:

答案 0 :(得分:0)

不,它不是线程安全的。线程安全以变量restnullptr的初始化结束。多个线程可以并行运行代码。

线程安全版本就是这样:

std::shared_ptr<ApiFacade> ApiFacade::GetInstance(const std::string & url) {
    // Initialization of function-local statics is guaranteed to occur only
    // once even when called from multiple threads, and may be more efficient
    // than the equivalent code using std::call_once.
    static std::shared_ptr<ApiFacade> rest = nullptr;

    if (rest == nullptr)
    {
        if (!url.empty())
        {
            std::shared_ptr<ApiFacade> empty_rest;
            std::atomic_compare_exchange_strong(&rest, &empty_rest, std::make_shared<ApiFacade>(url));
        }
        else
        {
            throw std::invalid_argument("Failed creating REST API object, Pass a valid URL");
        }
    }

    return rest;
}

但是如果它为空,这只会更新rest。我不确定这是否是理想的行为。

修改

我忘了提及,正如@ Jarod42所说,可能会构建多个ApiFacade类。由于与empty_rest的比较失败,只有一个人会被留下而其他人将被毁坏。

此外,我应该向其他人指出,这里仍然存在某种逻辑竞争形势。当第一次由多个线程调用该函数时,如果不同的线程使用不同的url参数,则在构造url时将成功使用随机rest,其他将被丢弃但是,除此之外,没有未定义的行为,因此代码块可以安全使用。