Это правильно? И правильно ли я предположить, что применение порядка памяти на std::atomic_flag
НЕ обеспечивает синхронизацию для блокировок общего назначения?
#include <atomic>
class Spinlock
{
public:
Spinlock(): f(ATOMIC_FLAG_INIT) {}
void lock()
{
while(f.test_and_set(std::memory_order_relaxed));
std::atomic_thread_fence(std::memory_order_acquire);
}
void unlock()
{
std::atomic_thread_fence(std::memory_order_release);
f.clear(std::memory_order_relaxed);
}
private:
std::atomic_flag f;
};
Извините, если это глупый вопрос, но я чувствую std::atmoic_thread_fence
Это необходимо для универсальной блокировки, и что применение memory_order_acquire
на test_and_set
а также memory_order_release
на clear
не достаточно, но я тоже не уверен.
Обычный шаблон заключается в использовании test_and_set(memory_order_acquire)
а также clear(memory_order_release)
, Но я подозреваю, что вы уже это знаете.
Согласно стандартному разделу 29.8 [atomic.fences] (2):
Разделительное ограждение A синхронизируется с ограждением получения B, если оно существует
атомарные операции X и Y, оба работают на некотором атомном объекте M,
так, что A секвенируется до X, X модифицирует M, Y секвенируется до
B, а Y читает значение, написанное X или значение, написанное любой стороной
эффект в гипотетической последовательности релиза X достиг бы, если бы это было
отпустить операцию.
В вашем коде А это забор в вашем unlock()
функция; Х является clear()
; Y забор в вашем lock()
функция; и B является test_and_set()
, Таким образом, ваш код соответствует требованиям этого раздела стандарта и, следовательно, ваш unlock()
а также lock()
функции правильно синхронизированы.
В целом это правильно.
Поскольку вы использовали ‘std :: memory_order_relaxed’ в функции test_and_set, то без вызова atomic_thread_fence нет ничего, что могло бы помешать переупорядочению операций, выполняемых до и после Mutex :: lock. Поскольку ожидается, что ‘Mutex :: lock’ действует как барьер памяти, тогда вызов ‘atomic_thread_fence’ становится необходимым. Но я полагаю, что тот же эффект может быть достигнут при использовании ‘std :: memory_order_acquire’ в ‘функции test_and_set’.
Видеть это:
http://en.cppreference.com/w/cpp/atomic/atomic_flag