C ++ Многопоточность: видимые побочные эффекты неатоми c переменных - PullRequest
2 голосов
/ 18 апреля 2020

В стандарте C ++ есть часть о многопоточной модели памяти, которую я не понимаю.

A видимый побочный эффект A на скалярном объекте или бите -поле M относительно вычисления значения B для M удовлетворяет условиям:

  • A происходит до B и

  • нет другого побочного эффекта от X до M, так что A происходит до X, а X происходит до B.

Значение non-atomi c скалярный объект или битовое поле M, как определено оценкой B, должно быть значением, хранимым видимым побочным эффектом A .

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

Теперь Предположим, что есть два потока T1 и T2, оба запущены основным потоком и никогда не выполняют никакой синхронизации друг с другом (поэтому не будет никаких « до того, как » установится связь между T1 и T2). Если T1 записывает в non-atomi c переменную M, то, согласно приведенной выше цитате, T2 никогда не должен видеть M, модифицированный T1, потому что " не происходит до " взаимосвязь между T1 и T2.

Вместо этого T2 имеет " синхронизируется с ", связь устанавливается с основным потоком в момент запуска T2, поэтому T2 должен видеть значение M, установленное основным поток до того, как он был запущен основным потоком, потому что между главным потоком и T2 существует отношение " происходит до ".

Верно? Однако я провел эксперимент на моей машине, и это было не так. Что не так?

1 Ответ

2 голосов
/ 18 апреля 2020

T1 записывает в неатоми c переменную M, тогда, согласно приведенной выше цитате, T2 никогда не должен видеть M, модифицированный T1

. Учитывайте следующее:

Два действия могут выполняться одновременно, если

  • они выполняются разными потоками или

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

Чтение T2 M и запись T1 в M "потенциально параллельны". Далее:

Два вычисления выражений конфликтуют, если одно из них изменяет ячейку памяти, а другое считывает или изменяет ту же ячейку памяти.

Чтение M T2 конфликтует с Запись T1 в M. Таким образом, эти «потенциально одновременные» действия конфликтуют.

Наконец, мы приходим к:

Выполнение программы содержит гонку данных, если она содержит два потенциально одновременных конфликтующие действия, по крайней мере одно из которых не является атомом c, и ни одно из них не происходит раньше другого, за исключением специального случая для обработчиков сигналов, описанного ниже. Любая такая гонка данных приводит к неопределенному поведению.

Чтение T2 M не происходит до записи T1 в M, равно как и запись T1 в M не происходит до чтения T2 M. Следовательно, у вас есть данные расы.

А гонки данных - это неопределенное поведение. Дело не в том, что T2 не увидит запись в M; это то, что он мог видеть все: старое значение, новое значение, какое-то третье значение, носовые демоны, выдающие вперед, что-нибудь .

...