Double-Checked Lock Singleton в C ++ 11

Свободна ли следующая синглтонная реализация от гонки данных?

static std::atomic<Tp *> m_instance;
...

static Tp &
instance()
{
    if (!m_instance.load(std::memory_order_relaxed))
    {
        std::lock_guard<std::mutex> lock(m_mutex);
        if (!m_instance.load(std::memory_order_acquire))
        {
            Tp * i = new Tp;
            m_instance.store(i, std::memory_order_release);    
        }    
    }

    return * m_instance.load(std::memory_order_relaxed);
}

Является ли std :: memory_model_acquire операции загрузки лишнее? Можно ли еще больше ослабить операции загрузки и сохранения, переключив их на std :: memory_order_relaxed ? В этом случае семантика получения / выпуска std :: mutex достаточна, чтобы гарантировать ее правильность, или еще один std :: atomic_thread_fence (std :: memory_order_release) также требуется для гарантировать, что запись в память конструктора происходит до расслабленного хранилища? Тем не менее, эквивалентно ли использование забора наличию магазина с memory_order_release ?

EDIT : Благодаря ответу Джона, Я придумал следующую реализацию, в которой не должно быть гонки данных. Несмотря на то, что внутренняя нагрузка может быть вообще не атомарной, я решил оставить расслабленную нагрузку, чтобы она не влияла на производительность. По сравнению с постоянной внешней загрузкой с порядком получения памяти, механизм thread_local улучшает производительность доступа к экземпляру примерно на порядок.

static Tp &
instance()
{
    static thread_local Tp *instance;

    if (!instance && 
        !(instance = m_instance.load(std::memory_order_acquire)))
    {
        std::lock_guard<std::mutex> lock(m_mutex);
        if (!(instance = m_instance.load(std::memory_order_relaxed)))
        {
            instance = new Tp; 
            m_instance.store(instance, std::memory_order_release);    
        }    
    }
    return *instance;
}
43
задан Xeo 18 December 2011 в 22:31
поделиться