我正在使用Singleton设计模式进行练习。从讨论here和here,我理解本地静态变量的初始化是自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;
}
答案 0 :(得分:0)
不,它不是线程安全的。线程安全以变量rest
与nullptr
的初始化结束。多个线程可以并行运行代码。
线程安全版本就是这样:
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
,其他将被丢弃但是,除此之外,没有未定义的行为,因此代码块可以安全使用。