Я выслушал и прочитал несколько статей, выступлений и вопросов о стековом потоке, касающихся std::atomic
, и я хотел бы быть уверен, что хорошо это понял. Потому что я все еще немного запутался с видимостью записи строк кэша из-за возможных задержек в протоколах когерентности кэша MESI (или производных), буферах хранения, недействительных очередях и так далее.
Я прочитал, что у x86 более сильная модель памяти, и что если задержка аннулирования кэша задержана, x86 может восстановить начатые операции. Но теперь меня интересует только то, что я должен считать программистом C ++ независимо от платформы.
[T1: thread1 T2: thread2 V1: shared atomi c переменная]
I понимать, что std :: atomi c гарантирует, что
(1) Никаких гонок данных в переменной не происходит (благодаря исключительному доступу к строке кэша).
(2) В зависимости от того, какой memory_order, который мы используем, гарантирует (с барьерами), что последовательная последовательность происходит (перед барьером, после барьера или обоими).
(3) После атома c запись (V1) на T1, атом c RMW (V1) на T2 будет согласованным (его строка кэша будет обновлена с записанным значением на T1).
Но, как учебник по когерентности кэша упомяните,
Смысл всех этих вещей заключается в том, что по умолчанию загрузки могут извлекать устаревшие данные (если соответствующий запрос аннулирования находился в очереди недействительности)
Итак, следующее правильно?
(4) std::atomic
НЕ ГУ Гарантируйте, что T2 не будет читать 'устаревшее' значение на атоме c read (V) после атоми c write (V) на T1.
Вопросы, если (4) верно: if запись atomi c в T1 делает недействительной строку кэша независимо от задержки, почему T2 ожидает, когда аннулирование вступит в силу, когда операция Atomi c RMW, но не на атоме c, читает?
Вопросы, если (4) неправильно: когда поток может прочитать «устаревшее» значение и «видно» в выполнении, тогда?
Я ценю ваши ответы очень много
Обновление 1
Так что, похоже, я ошибся в (3) тогда. Представьте себе следующее перемежение для начального V1 = 0:
T1: W(1)
T2: R(0) M(++) W(1)
Даже если в этом случае RMW T2 гарантированно произойдет полностью после W (1), он все равно может прочитать «устаревшее» значение (I был неправ). Согласно этому, atomi c не гарантирует полную когерентность кэша, только последовательную согласованность.
Обновление 2
(5) Теперь представьте себе этот пример (x = y = 0 и являются атомами c):
T1: x = 1;
T2: y = 1;
T3: if (x==1 && y==0) print("msg");
в соответствии с тем, что мы говорили, отображение «msg» на экране не даст нам информации, кроме того, что T2 был выполнен после T1. Таким образом, могло произойти любое из следующих исполнений:
это верно?
(6) Если поток всегда может прочитать «устаревшие» значения, что произойдет, если мы примем типичный сценарий «publi sh», но вместо сигнализируя, что некоторые данные готовы, мы делаем прямо противоположное (удаляем данные)?
T1: delete gameObjectPtr; is_enabled.store(false, std::memory_order_release);
T2: while (is_enabled.load(std::memory_order_acquire)) gameObjectPtr->doSomething();
, где T2 все еще будет использовать удаленный ptr, пока не увидит, что is_enabled имеет значение false.
( 7) Кроме того, тот факт, что потоки могут читать «устаревшие» значения, означает, что мьютекс не может быть реализован только с одним безблокировочным атомом c верно? Это потребовало бы механизма синхронизации между потоками. Требуется ли запираемый атоми c?