Подтвердить что ты не робот

С++ 11 memory_order_acquire и memory_order_release семантика?

http://en.cppreference.com/w/cpp/atomic/memory_order и другие онлайн-ссылки С++ 11, определите memory_order_acquire и memory_order_release как:

  • Операция получения: no reads в текущем потоке может быть переупорядочена перед этой загрузкой.
  • Операция Release: no write в текущем потоке может быть изменена после этого хранилища.

Это, по-видимому, позволяет выполнять операции post-purchase-write до операции получения, что кажется мне странным тоже (обычная семантика операции получения/выпуска ограничивает перемещение all операции с памятью).

Тот же источник в Интернете (http://en.cppreference.com/w/cpp/atomic/atomic_flag) предполагает, что мьютексы спин-спина могут быть построены с использованием атомарности С++ и вышеупомянутых правил упорядочения свободной памяти:

lock mutex: while (lock.test_and_set(std::memory_order_acquire))

unlock mutex: lock.clear(std::memory_order_release);               

С этим определением блокировки/разблокировки не будет нарушен простой код ниже, если memory_order_acquire/release действительно определены таким образом (т.е. не запрещает переупорядочение пост-приобретать-пишет):

Thread1:
  (0) lock
    (1) x = 1;
    (2) if (x != 1) PANIC
  (3) unlock

Thread2:
  (4) lock
    (5) x = 0;
  (6) unlock

Возможно ли следующее выполнение: (0) lock, (1) x = 1, (5) x = 0, (2) PANIC? Что я пропустил?

4b9b3361

Ответ 1

Внедрение мьютекса спин-спикера выглядит хорошо для меня. Я думаю, что они получили определения о приобретении и освобождении совершенно неправильно.

Вот ясное объяснение моделей согласованности/доступности, о которых я знаю: Gharachorloo; Lenoski; Лаудон; Gibbons; Гупта; Hennessy: согласованность и упорядоченность событий в масштабируемых многопроцессорных системах с общей памятью, Int'l Symp Comp Arch, ISCA (17): 15-26, 1990, doi 10.1145/325096.325102. (Doi находится за платой ACM. Фактическая ссылка - на копию, не находящуюся за платой.)

Посмотрите на Условие 3.1 в Разделе 3.3 и на прилагаемый рисунок 3:

  • до того, как разрешена обычная загрузка или доступ к хранилищу для выполнения в отношении любого другого процессора, все предыдущие запросы доступа должны быть выполнены, а
  • до того, как доступ к релизу разрешен по отношению к любому другому процессору, все предыдущие обычные загрузка и сохранение доступа должны выполняться, а
  • специальные обращения [последовательно] согласуются с уважением друг к другу.

Дело в том, что: приобретения и выпуски последовательно согласованы (все потоки во всем мире согласуются с порядком, в котором происходят приобретения и выпуски.) Все потоки во всем мире согласны с тем, что материал, который происходит между приобретением и выпуском по конкретному потоку, произошел между приобретением и выпуском. Но нормальным нагрузкам и магазинам после выпуска разрешено перемещать (либо аппаратными средствами, либо компилятором) над выпуском, а нормальные нагрузки и хранилища до того, как приобретателю разрешено перемещать (либо аппаратным, либо компилятором) после приобретения.

В стандарте С++ (я использовал ссылку на проект Jan 2012) соответствующий раздел - 1.10 (страницы с 11 по 14).

Определение "случится раньше" должно быть смоделировано после Lamport; Время, часы и порядок событий в распределенной системе, CACM, 21 (7): 558-565, июль 1978 г.. С++ приобретает соответствующий Lamport, релизы С++ соответствуют Lamport send. Lamport разместил полный порядок в последовательности событий в одном потоке, где С++ должен допускать частичный порядок (см. Раздел 1.9, абзацы 13-15, стр. 10 для определения С++, описанного ранее). Тем не менее, перед заказом в значительной степени то, что вы ожидаете. Выражения упорядочены в порядке, указанном в программе. Раздел 1.9, параграф 14: "Каждое вычисление значения и побочный эффект, связанный с полным выражением, секвенируются перед каждым значением вычисление и побочный эффект, связанные со следующим полным выражением, которое должно быть оценено. "

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

В разделе 1.10, параграф 21 (стр. 14) говорится: "Программа не является бескабельной, если есть пара обращений A и B от разных потоков к объекту X, по крайней мере один из этих обращений имеет побочный эффект, и ни один из них не происходит - до B, а не B - до A. В противном случае программа будет недоступна для рассылки данных.

В пунктах 6-20 дается очень тщательное определение отношения, которое происходит до этого. Определение ключа - это пункт 12:

"Оценка A происходит до оценки B, если:

  • A секвенирован до B или
  • Межпоточная передача происходит до B. "

Итак, если сборка секвенируется до (в том же потоке) почти любой другой оператор, то, вероятно, приобретение должно произойти до этого утверждения. (Включая, если этот оператор выполняет запись.)

Аналогично: если почти любая инструкция секвенирована до (в том же потоке) выпуска, то этот оператор должен появиться до релиза. (Включая, если этот оператор просто выполняет вычисление значения (чтение).)

Причина, по которой компилятору разрешено переносить другие вычисления после релиза до релиза (или до приобретения после приобретения) из-за того, что эти операции специально не имеют межпоточных, перед отношениями (потому что они находятся за пределами критического раздела). Если они участвуют в семантике undefined, и если они не участвуют в гонке (потому что они не разделены), вы не можете точно сказать, когда они произошли в отношении синхронизации.

Это очень длинный способ сказать: определения cppreference.com о приобретении и выпуске ошибочны. В вашей примерной программе нет условия гонки данных, и PANIC не может произойти.