c++中的double check


  double check 在并发编程中经常⻅到,很多⼈会⽤,但是仅有double check还是不够的,会产⽣reorder的问题;拿单例模式中的double check中get_instance接⼝来举例,如下:

<code>singleton& get_instance() { 
if (m_instance_ptr == nullptr) {
std::lock_guard<:mutex> mu_lock(singleton::m_mutex);
if (m_instance_ptr == nullptr) {
m_instance_ptr = new singleton();
}
}
return *m_instance_ptr;
}/<code>

  错误有可能发⽣在 m_instance_ptr = new singleton() ,这条语句并不是原⼦操作,我们可以简单理解为(分配内存,构造函数调⽤,赋值给 m_instance_ptr )三步。

  ⽽这三部分有可能会在编译器中或者执⾏单元中优化的时候改变顺序,对于临界区的代码来说没有问题,但是当顺序改变成为(分配内存,赋值给 m_instance_ptr ,构造函数调⽤)时,加上第⼀层check就有问题;当执⾏到赋值给 m_instance_prt 时,这时还没有调⽤构造函数,另⼀个并发执⾏到第⼀个check,直接返回了未执⾏构造函数的对象,将出现为定义的⾏为。⽽在⼤多数的语⾔中都会有这种reorder的问题。

为了应对此类问题,java引⼊volidate关键字;但是c++却没有能达到同等效果的关键字;

⽽在c++11之后我们可以有⼏种⽅法来解决这个问题;

  • 使⽤std::call_once
<code>std::once_flag m_initflag;
singleton& get_instance() {
std::call_once(m_initflag, [&](){m_instance_ptr = new singleton();});
return *m_instance_ptr;
}/<code>
  • 静态局部变量

According to the standard, §6.7 [stmt.dcl] p4

Visual Studio: supported since Visual Studio 2015

GCC: supported since GCC 4.3

<code>singleton& get_instance() {
static singleton s;
return s;
}/<code>
  • 原⼦变量+内存序
<code>std::atomic<singleton> m_instance;
singleton* get_instance() {
singleton* tmp = m_instance.load(std::memory_order_acquire);
if (tmp == nullptr) {
std::lock_guard<:mutex> lock(m_mutex);
tmp = m_instance.load(std::memory_order_relaxed);
if (tmp == nullptr) {
tmp = new singleton;
m_instance.store(tmp, std::memory_order_release);
}
}
return tmp;
}/<singleton>/<code>

参考:

C++ Singleton design pattern

Double-check locking issues, c++

Is Meyers' implementation of the Singleton pattern thread safe?


分享到:


相關文章: