2017-04-13 84 views
0

我正在做一个单身设计模式的练习。从讨论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; 
} 
+0

我投票作为题外话,因为这个问题属于http://codereview.stackexchange.com – user3159253

+0

'静态的std :: shared_ptr的休息=的std :: make_shared (URL)来关闭这个问题;返回休息;'。你的版本不是thread_safe。 – Jarod42

+0

@ Jarod42,我明白,但我需要在创建'std :: shared_ptr'之前验证'url'。 – Panch

回答

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的比较失败,只有一个人会留下,其他人将被毁坏。

另外,我应该指出其他人在这里仍然存在某种合乎逻辑的race-y情况。当函数首次被多个线程调用时,如果不同的线程使用不同的url参数,则在构建rest时将成功使用随机url,而其他线程将被丢弃。但是,除此之外,没有未定义的行为,所以代码块可以安全使用。

+0

是的,“休息”只需要初始化一次。 – Panch

+0

不是几个'ApiFacade'可以在这里构造。 – Jarod42