В частности, меня смущают два предложения из cppreference о последствиях порядка выпуска-приобретения:
Все записи в память (включая неатомарные и расслабленные атомарные), которые произошли до атомарного хранилища с точки зрения потока A, становятся видимыми побочные эффекты в потоке B. То есть, как только атомарная загрузка завершена, поток B гарантированно увидит все, что поток A записал в память.
Из приведенной выше цитаты явно не указаны какие-либо гарантии видимости записей (атомарных или нет), которые не выполняются в потоке A, но также произошли до чтения в потоке B, поэтому это означает, что нет вообще гарантирует (для неатомарных записей)?
Рассмотрим следующий пример:
- у нас есть 3 потока
- переменные X и Y являются атомарными целыми числами, инициализированными значением 0
- переменная A может быть атомарным целым числом или просто обычным целым числом (зависит от случая/вопроса), и его инициализация равна 0
Код: Выделить всё
Thread 1: | Thread 2: | Thread 3:
-------------------------------------------------------------------
write 10 to A (WA) | |
X.write(1, release) | |
| X.load(acquire), returns 1 |
| read A (RA1) |
| Y.write(2, release) |
| | Y.load(acquire), returns 2
| | read A = ? (RA2)
Независимо от типа чтения/записи, выполняемого для A, у нас есть следующее (почти уверен, что я правильно понял отношения):
Код: Выделить всё
Thread 1: | Thread 2: | Thread 3:
-------------------------------------------------------------------
write 10 to A (WA) | |
| (SeqBef) | |
\/ | |
X.write(1, release) ---------| (SyncWith) |
| \/ |
| X.load(acquire), returns 1 |
| | (SeqBef) |
| \/ |
| read A, must return 10 (RA1) |
| | (SeqBef) |
| \/ |
| Y.write(2, release) ------------| (SyncWith)
| | \/
| | Y.load(acquire), returns 2
| | | (SeqBef)
| | \/
| | read A = ? (RA2)
Порядок происходит до того, как определяется (для простоты я буду использовать определение C++26, поскольку оно более краткое):
Происходит до
Независимо от потоков, оценка A происходит до оценки B, если верно любое из следующих условий:
- A является упорядоченным до B.
- A синхронизируется с B.
- A происходит до X, а X происходит до B.
Код: Выделить всё
WAКод: Выделить всё
RA2Теперь рассмотрим случаи в A:
- (Case_A 1) операции записи и чтения в A неатомарны.
- (Case_A 2) записи и чтения в A являются атомарными (можно ослабить, получить/освободить или seq_cst)
- происходит до RA2.
Код: Выделить всё
WA - согласованность записи и чтения утверждает, что если атомарная запись () происходит перед чтением (
Код: Выделить всё
WA), то такое чтение должно возвращать значение, которое следует за записью в порядке модификации атомарной переменнойКод: Выделить всё
RA2
поскольку между WA и RA2 не было записей, RA2 должно видеть значение, записанное WA, которое равно 10.
Но для Case_A 1, я не знаю, что может произойти: я не могу сделать какие-либо выводы из стандарта и cppreference, которые RA2 гарантированно увидит или не увидит 10:
- единственное отношение, которое у меня есть, это WA происходит до RA2 в Case_A 1
- похоже, что я могу вызвать определение видимых побочных эффектов, где WA происходит раньше, чем RA2 подразумевает, что WA является видимым побочным эффектом для RA2, следовательно, RA2 должен видеть 10, но я не уверен, правильно ли это?
- A происходит до B.
- Нет другого побочного эффекта от X к M, где A происходит до X, а X происходит до B.
- добавится ли атомic_thread_fence() любого порядка памяти (даже seq_cst) сразу после WA и прямо перед RA1 и RA2, гарантирующими, что RA2 должен видеть 10? Если безatomic_thread_fence это невозможно, не думаю, что добавлениеatomic_thread_fence поможет получить какие-то дополнительные гарантии?
Подробнее здесь: https://stackoverflow.com/questions/798 ... ens-before
Мобильная версия