2015-11-30 6 views
0

Так что я кое-что прочитал: https://en.wikipedia.org/wiki/Double-checked_locking и http://preshing.com/20130930/double-checked-locking-is-fixed-in-cpp11/. Я нашел этот код для его использованияДвойная проверка блокировки: Заборы и атомизация

std::atomic<Singleton*> Singleton::m_instance; 
std::mutex Singleton::m_mutex; 

Singleton* Singleton::getInstance() { 
    Singleton* tmp = m_instance.load(std::memory_order_relaxed); 
    std::atomic_thread_fence(std::memory_order_acquire); 
    if (tmp == nullptr) { 
     std::lock_guard<std::mutex> lock(m_mutex); 
     tmp = m_instance.load(std::memory_order_relaxed); 
     if (tmp == nullptr) { 
      tmp = new Singleton; 
      std::atomic_thread_fence(std::memory_order_release); 
      m_instance.store(tmp, std::memory_order_relaxed); 
     } 
    } 
    return tmp; 
} 

и есть одно, что мне непонятно. Работает ли он иначе, чем следующий код без заборов?

std::atomic<Singleton*> Singleton::m_instance; 
std::mutex Singleton::m_mutex; 

Singleton* Singleton::getInstance() { 
    Singleton* tmp = m_instance.load(std::memory_order_acquire); 
    if (tmp == nullptr) { 
     std::lock_guard<std::mutex> lock(m_mutex); 
     tmp = m_instance.load(std::memory_order_acquire); 
     if (tmp == nullptr) { 
      tmp = new Singleton; 
      m_instance.store(tmp, std::memory_order_release); 
     } 
    } 
    return tmp; 
} 

Что я имею в виду, если я заменяю заборы соответствующим порядком памяти в load/store, работает ли он так же?

+3

Удобно, когда вы используете C++ 11, есть ** нет ** нужно использовать любое из этого! Просто используйте это: 'Singleton * Singleton :: getInstance() {static Singleton rc; return &rc;} 'Функционально-локальные переменные' static' инициализируются в потокобезопасном режиме для начала. Обратите внимание, что этот комментарий не считается одобрением синглтонов: он является анти-шаблоном, и, на мой взгляд, нет места для одиночек, тем более в параллельном коде. –

+0

У этой конструкции есть два недостатка для меня: 1) в Visual Studio 2012 это не фактически безопасный поток AFAIK 2) вы не можете избавиться от созданного таким образом одноэлементного кода до конца вашей программы – Paladin

+1

Если вы действительно хотите «удалить» объект , вы * можете * использовать указатель и выделить его: инициализация по-прежнему является потокобезопасной: 'static Singleton * rc = new Singleton();'. Независимо от того, инициализирует ли MSVC++ 2012 функцию локальную статику или нет, я не знаю: я предпочитаю не использовать разбитые компиляторы. –

ответ

1

Разница между двумя конструктами объясняется в последующей статье на том же сайте: Acquire and Release Fences Don't Work the Way You'd Expect. В принципе, забор гарантирует, что все атомные магазины после забора будут видны «не раньше» во всех магазинах перед забором. Хранилище с параметром memory_order_release делает такую ​​гарантию только для магазинов переменной, связанной с инструкцией магазина.

В вашем примере у вас есть только один атом, m_instance, поэтому две конструкции функционально эквивалентны, а одна без ограждений, вероятно, более эффективна.

Смежные вопросы