Код: Выделить всё
#include
#include
#include
#include
#include
int main(){
std::atomic v = 0;
std::atomic flag = false;
std::thread t1([&](){
while(!flag.load(std::memory_order::relaxed)){} // #1
assert(v.exchange(2,std::memory_order::relaxed) == 1); // #2
});
std::thread t2([&](){
if(v.exchange(1,std::memory_order::relaxed) == 0){ // #3
flag.store(true, std::memory_order::relaxed); // #4
}
});
t1.join();
t2.join();
}
Атомарные операции чтения-изменения-записи всегда должны считывать последнее значение ( в порядке модификации), записанном перед записью, связанной с операцией чтения-изменения-записи.
Это означает, что никакие другие операции RMW не могут прочитать значение 0. , если операция RMW в #3 читает 0. Другими словами, если бы #2 мог читать 0 и записывать 2, #3 не читал бы 0, а #4 не читал бы. быть исполнено. Другими словами, значение чтения операции чтения-изменения-записи уникально принадлежит этой операции, если все остальные операции также являются операциями RMW (это суть того, как может работать спин-блокировка).
Итак, Q1: утверждение №2 никогда не подведет, верно?
Однако, если #2< /code> заменяется на чистую загрузку, примерно так:
Код: Выделить всё
assert(v.load(std::memory_order::relaxed) == 1); // #2'
Если побочный эффект X на атомарном объекте M происходит до того, как вычисление значения B для M, тогда оценка B берет свое значение из X или из побочного эффекта Y, который следует за X в порядке модификации M.
Побочный эффект, который происходит перед #2', представляет собой только начальное значение 0, хотя побочный эффект 1, сохраненный в #3, следует за 0 в порядок модификации, чистая загрузка все еще может читать 0, поскольку [intro.races] p18 использует «или», что также подразумевается в [atomics.order] p11
Рекомендуемая практика: реализация должна сделать атомарные хранилища видимыми для атомных загрузок, а атомарные загрузки должны наблюдать за атомными хранилищами в течение разумного периода времени.
С точки зрения реализации существует временная задержка, из-за которой хранилище в #3 становится невидимым для #2' в течение разумного времени. Этот результат также подразумевается знаком «или» в [intro.races] p18 с точки зрения стандарта C++.
Q2:
Если Операция RMW в #2 заменяется на чистую загрузку, например #2', утверждение может завершиться неудачей, верно?
Q3:
если #2' может дать сбой, а #2 никогда не дает сбоя означает ли это, что RMW менее склонен к чтению устаревших значений, чем операции чтения без RMW, по крайней мере, в этом примере ?, Означает ли это, что RMW более склонен к чтению последней модификации в порядке модификации, чем к чтению без RMW, по крайней мере, в этом примере?
дополнение:
Я не думаю, что #2 может быть переупорядочен компилятором с #1, поскольку #2 аналогичен сбою CAS в спин-блокировке (т. е. представляет собой чистую загрузку с ослабленным порядком памяти), если такое переупорядочение существует, спин-блокировка также не будет работать. Более того, любое переупорядочение компилятором в этом примере заметно благодаря этому утверждению. Однако с точки зрения порядка памяти #3 не происходит раньше #2 и наоборот, что теоретически означает, что #3 может привести к сбою. Я не уверен. Однако этот пример зависит от логического порядка выполнения, любое разрушение порядка заметно.
Примечание:
Это следующий вопрос: гарантирует ли часть загрузки операции чтения-изменения-записи атомарного объекта чтение последнего значения в порядке модификации по сравнению с операцией загрузки?, который имеет неясный пример и непонятное предположение, которые в этом вопросе улучшены и понятны.
Подробнее здесь: https://stackoverflow.com/questions/791 ... -a-pure-lo
Мобильная версия