Может кто-нибудь объяснить, как ((a == 1 && a == 2 && a == 3) == true)? - PullRequest
0 голосов
/ 02 октября 2018

Основываясь на информации о выходе, кто-нибудь может объяснить код ниже?

Как все (a==1 && a==2 && a==3) может быть правдой?

#include <iostream>
#include <thread>

int a = 0;

int main()
{
  std::thread runThread([]() { while (true) { a = 1; a = 2; a = 3; }});
  while (true)
  {
    if (a == 1 && a == 2 && a == 3)
    {
      std::cout << "Hell World!" << std::endl;
    }
  }
}

Вывод:

Hell World!
Hell World!
Hell World!
Hell World!
Hell World!
Hell World!
Hell World!
Hell World!
Hell World!
Hell World!
Hell World!
Hell World!
Hell World!
Hell World!
Hell World!
Hell World!
Hell World!

...


Январь 2019

Я думаю, что этот вопрос очень актуален для этой ссылки -> C ++ 11 ввел стандартизированную модель памяти.Что это значит?И как это повлияет на программирование на C ++?

Ответы [ 2 ]

0 голосов
/ 02 октября 2018

Вы испытываете неопределенное поведение.

Ваш код имеет состояние гонки;один поток читает a, в то время как другой пишет, и синхронизация не происходит.Вам не разрешено делать это в C ++.Программы, которые делают это, могут быть скомпилированы так, чтобы делать что угодно .

На самом деле, при сборке релиза, я ожидаю, что компиляция до if(false).Компилятор оптимизирует основной поток, не замечает никакой синхронизации, доказывает, что a не может иметь 3 различных значения без UB, и оптимизирует if.

В отладочной сборке я мог ожидать появления симптомов, которые вы видите.Не потому, что это более правильно, а потому, что отладочные сборки, как правило, не допускают такого неопределенного поведения.

Итак, первое, что вы должны сделать, чтобы разумно рассказать о своей программе, это удалить неопределенное поведение: make a a std::atomic<int> вместо int.

Теперь и в выпуске, и в отладке вы ожидаете увидеть ... именно то, что показал ваш тест.Или ничего.Или что-нибудь промежуточное.Результат больше не является неопределенным, но он остается недетерминированным.

Оператор if не является атомарным.Между условиями a может изменить .И с программой, выполняющейся вечно, это может происходить иногда, так как другой поток меняет ее. Даже гарантии прогресса вперед в порядке, потому что вы читаете атомарную переменную.

0 голосов
/ 02 октября 2018

Вы, вероятно, скомпилировали свою программу с отключенной оптимизацией, поэтому сборка / машинный код для целевой архитектуры, для которой вы компилируете, фактически выполняет все шаги на абстрактной машине C ++ в том порядке, в котором они отображаются, включая фактическое сохранение или загрузку в/ из оперативной памяти.(Сборки отладки обычно обрабатывают каждую переменную как volatile.)

На типичных архитектурах, где 32-разрядная int загрузка / сохранение является естественным атомом, сборка отладки ведет себя подобно переносимой программе на C ++ без неопределенныхповедение с использованием std::atomic<int> a с a.store(1, std::memory_order_relaxed) (или на x86, std::memory_order_release).


Программа запускает поток, который многократно устанавливает значение a в 1, затем 2, затем 3. (Этоявляется строкой runThread).

Затем главный поток проверяет, равно ли a 1 и a равно 2 и a равно 3. И печатает «hello world», если он равен всем трем.(это происходит во втором while(true))

Причина, по которой он печатает "Hello World", заключается в параллельности.Бывает, что, когда один поток выполняет три теста, другой поток записывает правильные значения как раз в нужное время.Помните, что три части if выполняются одна за другой


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

Не полагайтесь на это поведение.Вместо этого попытайтесь понять, что два потока выполняют операции одновременно.

...