Есть ли у нас гарантия, что любая атомарная запись немедленно сохранит новое значение атомарной переменной в основной памяти?

Итак, я много читал о переупорядочении инструкций и памяти и о том, как мы можем предотвратить это, но у меня до сих пор нет ответа на один вопрос (возможно, потому что я недостаточно внимателен). У меня вопрос: есть ли у нас гарантия, что любая атомарная запись немедленно сохранит новое значение атомарной переменной в основной памяти? Давайте посмотрим на небольшой пример:

std::atomic<bool> x;
std::atomic<bool> y;
std::atomic<int> count;
void WritingValues()
{
   x.store(true, std::memory_order_relaxed);
   y.store(true, std::memory_order_relaxed);
}
void ReadValues()
{
   while( !y.load(std::memory_order_relaxed) );
   if( x.load(std::memory_order_relaxed) )
       ++count;
}
int main()
{
   x = false;
   y = false;
   count = 0;
   std::thread tA(WritingValues);
   std::thread tB(ReadValues);
   tA.join();
   tB.join();
   assert( count.load() != 0 );
}

Итак, здесь наш assert может определенно срабатывать, так как мы используем std::memory_order_relaxed и не предотвращаем переупорядочение команд (или переупорядочение памяти во время компиляции, я полагаю, это одно и то же). Но если мы поместим некоторый барьер компилятора в WritingValues ​​для предотвращения переупорядочения команд, все ли будет хорошо? Я имею в виду, гарантирует ли x.store(true, std::memory_order_relaxed), что запись этой конкретной атомарной переменной будет осуществляться непосредственно в память без каких-либо задержек? Или x.load(std::memory_order_relaxed) гарантирует, что значение будет считано из памяти, а не из кэша с недопустимым значением? Другими словами, это хранилище гарантирует только атомарность операции и имеет то же поведение памяти, что и обычная неатомарная переменная, или оно также влияет на поведение памяти?

2 ответа

Решение
 I mean, does x.store(true, std::memory_order_relaxed) guarantees, that the  
 of that particular atomic variable will be directly into the memory,  
 without any latency?  

Нет, это не так, и на самом деле, с учетом того, что порядок bool и памяти ослаблен, нет "недопустимого" значения, если вы читаете его только один раз, и истина, и ложь в порядке.
Поскольку упорядоченный порядок в памяти явно указывает, что порядок не выполняется. По сути, в вашем случае это означает только то, что после переключения с ложного на истинное значение в какой-то момент станет истинным для всех других процессов, но не указывает, "когда" это произойдет. Таким образом, единственное, в чем вы можете быть уверены, это то, что оно не станет ложным после того, как станет истинным. Но нет никаких ограничений на то, как долго он будет ложным в другом потоке.
Также это гарантирует, что вы не увидите частично записанную переменную в другом потоке, но это вряд ли относится к bools.
Вам нужно использовать aquire и выпустить здесь. И даже это не даст никаких гарантий относительно реальной памяти, только о поведении программы, синхронизация кеша может добиться цели, даже не возвращая данные назад и не всплывая в памяти.

Поскольку все инструкции загрузки и сохранения являются атомарными, каждая из них представляет собой отдельную машинную инструкцию, поэтому два потока никогда не "прерывают друг друга" в "середине" инструкции загрузки или сохранения.

Заголовок вашего вопроса звучит так: "Есть ли у нас гарантия, что любая атомарная запись немедленно сохранит новое значение атомарной переменной в основной памяти?". Но само определение атомарной инструкции состоит в том, что она не может быть прервана переключением контекста, аппаратным прерыванием, программным ожиданием - ничто!

std::memory_order_relaxed позволяет переупорядочить инструкции в одной функции. Смотрите, например, этот вопрос. Это почти так же, как ваш вопрос, но у вас есть memory_order_relaxed в ReadValues() вместо memory_order_acquire, В этой функции возможно, что спинлок на переменную y помещается после увеличения счетчика из-за расслабленного состояния (переупорядочивание компилятора и т. д.). В любом случае ASSERT может потерпеть неудачу, потому что y может быть установлен в true, прежде чем x находится в WriteValues() из-за переупорядочения памяти, разрешенного memory_order_relaxed (ссылаясь на ответ в аналогичном вопросе.

Другие вопросы по тегам