Я выслушал и прочитал несколько статей, выступлений и вопросов о стековом потоке std::atomic
и хотел бы быть уверен, что хорошо это понял. Потому что я все еще немного запутался с видимостью записи строк кэша из-за возможных задержек в протоколах когерентности кэша MESI (или производных), буферах хранения, недействительных очередях и так далее.
Я читал, что у x86 более сильная модель памяти, и что, если аннулирование кэша задерживается, x86 может восстановить запущенные операции. Но сейчас меня интересует только то, что я должен считать программистом C ++, независимо от платформы.
[T1: поток1 T2: поток2 V1: общая атомарная переменная]
Я понимаю, что std :: atomic гарантирует, что
(1) В переменной не происходит скачек данных (благодаря эксклюзивному доступу к строке кэша).
(2) В зависимости от того, какой memory_order мы используем, он гарантирует (с барьерами), что последовательная согласованность происходит (до барьера, после барьера или обоих).
(3) После атомарной записи (V1) в T1, атомарный RMW (V1) в T2 будет когерентным (его строка кэша будет обновлена с записанным значением в T1).
Но, как упомянуть пример когерентности кеша ,
Следствием всего этого является то, что по умолчанию загрузки могут извлекать устаревшие данные (если в очереди недействительности находился соответствующий запрос на аннулирование)
Итак, верно ли следующее?
(4) std::atomic
НЕ гарантирует, что T2 не будет считывать «устаревшие» значения при атомарном чтении (V) после атомарной записи (V) в T1.
Вопросы, если (4) верно: если атомарная запись на T1 делает недействительной строку кэша независимо от задержки, почему T2 ожидает, чтобы аннулирование было эффективным, когда выполняется атомарная операция RMW, но не на атомарном чтении?
Вопросы, если (4) неверно: когда поток может прочитать «устаревшее» значение и «это видно» при выполнении, тогда?
Я очень ценю ваши ответы
Обновление 1
Так что, похоже, я ошибся в (3) тогда. Представьте себе следующее чередование для начального V1 = 0:
T1: W(1)
T2: R(0) M(++) W(1)
Несмотря на то, что RMW T2 гарантированно произойдет полностью после W (1) в этом случае, он все равно может считывать «устаревшее» значение (я был не прав). В соответствии с этим, atomic не гарантирует полную когерентность кэша, только последовательную согласованность.
Обновление 2
(5) Теперь представьте этот пример (x = y = 0 и являются атомарными):
T1: x = 1;
T2: y = 1;
T3: if (x==1 && y==0) print("msg");
согласно тому, что мы говорили, отображение «сообщения» на экране не даст нам информации, кроме того, что T2 был выполнен после T1. Так что могло произойти любое из следующих казней:
- Т1 <Т3 <Т2
- T1 <T2 <T3 (где T3 видит x = 1, но еще не y = 1)
это правильно?
(6) Если поток всегда может прочитать «устаревшие» значения, что произойдет, если мы выберем типичный сценарий «публикации», но вместо того, чтобы сигнализировать, что некоторые данные готовы, мы делаем наоборот (удаляем данные)?
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) Кроме того, тот факт, что потоки могут читать «устаревшие» значения, означает, что мьютекс не может быть реализован только с одним атомарным правом без блокировки? Это потребовало бы механизма синхронизации между потоками. Требуется ли блокируемый атом?