CompareAndExchange на трех атомарных переменных
Я хочу сравнить и обменять 3 атомные переменные:
std::atomic<int> a;
std::atomic<int> expected;
std::atomic<int> new;
int expectedValue = std::atomic_load_explicit(&expected, std::memory_order_relaxed);
int newValue = std::atomic_load_explicit(&new, std::memory_order_relaxed);
std::atomic_compare_exchange_strong_explicit(
&a,
&expectedValue,
newValue,
std::memory_order_relaxed,
std::memory_order_relaxed);
Но если между чтением expected
а также new
переменные и сравнивая их с a
, один другой поток изменяет свои значения, текущий поток будет работать с предыдущими значениями, поэтому я изменяю код на него:
while(true)
{
int expectedValue = std::atomic_load_explicit(&expected, std::memory_order_relaxed);
int newValue = std::atomic_load_explicit(&new, std::memory_order_relaxed);
std::atomic_compare_exchange_strong_explicit(
&a,
&expectedValue,
newValue,
std::memory_order_relaxed,
std::memory_order_relaxed);
int newExpectedValue = std::atomic_load_explicit(&expected, std::memory_order_relaxed);
int newNewValue = std::atomic_load_explicit(&new, std::memory_order_relaxed);
if(newExpectedValue == expectedValue && newNewValue == newValue)
break;
}
Мой код правильный? или есть лучший способ сделать это?
2 ответа
Ваша переписанная функция все еще может давать противоречивые результаты. Что, если expected
меняется после загрузки в newExpectedValue
, но прежде чем проверять, newExpectedValue == expectedValue
? Что, если new
а также expected
изменить после загрузки expected
, но прежде new
?
Это не то, как атомика предназначена для использования. Если вам нужно сделать операцию, включающую три переменные атомарно, вы должны использовать блокировку для сериализации доступа во время операции. Мьютекс или спин-блокировка были бы более подходящими здесь.
Почему expected
а также new
Атомная на первом месте? Как правило, вы вычисляете новое значение каким-то образом в каком-то потоке, и только этот поток знает новое значение и выполняет compare_exchange. Точно так же значение expected
это старое значение до того, как этот поток начал свои вычисления, но это ожидаемое, старое значение снова важно только для этого одного потока.
Короче: expected
а также new
не должны быть разделены между потоками.