Вызов в группе не возвращается до успешного завершения вышеупомянутого выполнения выбранной функции.
тся ли следующая гонка данных единственной реализацией бесплатной?
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;
}