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

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

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?

РЕДАКТИРОВАТЬБлагодаря ответу Джона, я разработал следующую реализацию, которая должна быть свободной от гонки данных. Хотя внутренняя нагрузка может быть вообще не атомарной, я решил оставить расслабленную нагрузку, поскольку она не влияет на производительность. По сравнению с тем, чтобы всегда иметь внешнюю нагрузку с порядком получения памяти, механизм 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;
}

Ответы на вопрос(0)

Ваш ответ на вопрос