Вопрос о гарантиях видимости операций с памятью происходит перед заказом в C++C++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 Вопрос о гарантиях видимости операций с памятью происходит перед заказом в C++

Сообщение Anonymous »

Как указано в заголовке, у меня вопрос о видимости гарантий работы с памятью, которые происходят до заказа. Я прочитал порядок памяти cppref и предыдущий ISO-проект C++, но до сих пор не могу с уверенностью сказать, что понимаю гарантии распространения видимости.
В частности, меня смущают два предложения из 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)
Я буду обозначать SeqBef, как указано выше, и SyncWith, как синхронизируется с.
Независимо от типа чтения/записи, выполняемого для 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)
(Обратите внимание, что в RA1 чтение A в потоке 2 должно возвращать 10 из-за приведенной выше кавычки порядка освобождения-получения: поток 1 явно записывает в A в WA, а WA происходит до RA1, поэтому WA должен быть видимым побочным эффектом для RA1, следовательно, RA1 видит значение, записанное WA)
Порядок происходит до того, как определяется (для простоты я буду использовать определение C++26, поскольку оно более краткое):

Происходит до
Независимо от потоков, оценка A происходит до оценки B, если верно любое из следующих условий:
  • A является упорядоченным до B.
  • A синхронизируется с B.
  • A происходит до X, а X происходит до B.
Это означает (согласно выводам выше), что запись в A () в потоке 1 происходит до чтения A () в потоке 3, независимо от типа операций записи и чтения в A.
Теперь рассмотрим случаи в A:
  • (Case_A 1) операции записи и чтения в A неатомарны.
  • (Case_A 2) записи и чтения в A являются атомарными (можно ослабить, получить/освободить или seq_cst)
Из определения согласованности записи и чтения и переполнения стека link1 и link2 я считаю, что Case_A 2 будет гарантировать RA2 чтение 10, потому что:
  • происходит до RA2.
  • согласованность записи и чтения утверждает, что если атомарная запись () происходит перед чтением (), то такое чтение должно возвращать значение, которое следует за записью в порядке модификации атомарной переменной

    поскольку между WA и RA2 не было записей, RA2 должно видеть значение, записанное WA, которое равно 10.

Но для Case_A 1, я не знаю, что может произойти: я не могу сделать какие-либо выводы из стандарта и cppreference, которые RA2 гарантированно увидит или не увидит 10:
  • единственное отношение, которое у меня есть, это WA происходит до RA2 в Case_A 1
  • похоже, что я могу вызвать определение видимых побочных эффектов, где WA происходит раньше, чем RA2 подразумевает, что WA является видимым побочным эффектом для RA2, следовательно, RA2 должен видеть 10, но я не уверен, правильно ли это?
Побочный эффект A на скаляре M (запись) виден в отношении вычисления значения B на M (чтение), если оба следующих условия: true:
  • A происходит до B.
  • Нет другого побочного эффекта от X к M, где A происходит до X, а X происходит до B.
потому что из определения видимых побочных эффектов это по сути то же самое, что происходит до, с ограничением, что никакие записи X не происходят между A и Б...
  • добавится ли атомic_thread_fence() любого порядка памяти (даже seq_cst) сразу после WA и прямо перед RA1 и RA2, гарантирующими, что RA2 должен видеть 10? Если безatomic_thread_fence это невозможно, не думаю, что добавлениеatomic_thread_fence поможет получить какие-то дополнительные гарантии?
Большое спасибо за прочтение

Подробнее здесь: https://stackoverflow.com/questions/798 ... ens-before
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»