http://en.cppreference.com/w/cpp/atomic/memory_order, и другие онлайн-ссылки на C ++ 11, определяющие memory_order_acquire и memory_order_release как:
Это, кажется, позволяет выполнять пост-приобретение-записи до Операция получения, которая кажется мне слишком странной (обычная семантика операции получения / выпуска ограничивает перемещение все операции с памятью).
Тот же онлайн источник (http://en.cppreference.com/w/cpp/atomic/atomic_flag) предполагает, что мьютекс со спин-блокировкой может быть построен с использованием атомарности C ++ и вышеупомянутых правил упорядочения памяти:
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) блокировка, (1) x = 1, (5) x = 0, (2) PANIC? Что я упустил?
Реализация спин-мьютекса выглядит хорошо для меня. Я думаю, что они получили определения приобретать а также релиз совершенно неправильно.
Вот ясное объяснение моделей согласованности получения / выпуска, о которых я знаю: 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:
Дело в том, что приобретения и выпуски последовательно согласованы (все потоки глобально согласовывают порядок, в котором происходили приобретения и выпуски.) Все потоки глобально соглашаются, что вещи, которые происходят между приобретением и выпуском в определенном потоке, происходили между приобретением и отпустить. Но нормальные нагрузки и магазины после выпуск может быть перемещен (либо аппаратно, либо компилятором) над релизом, а также обычными загрузками и хранилищами до приобретение может быть перемещено (или аппаратно, или компилятором) после приобретения.
в Стандарт C ++ (Я использовал ссылку на проект за январь 2012 года) соответствующий раздел — 1.10 (страницы с 11 по 14).
Определение случается, перед тем предназначен для моделирования после Лампорт; Время, часы и порядок событий в распределенной системе, CACM, 21 (7): 558-565, июль 1978 г.. C ++ приобретает соответствуют Лампорту получает, C ++ релизы соответствуют Лампорту посылает. Лэмпорт установил полный порядок последовательности событий в одном потоке, где C ++ должен разрешить частичный порядок (определение разделов C ++ в разделе 1.9, параграфы 13-15, стр. 10) секвенировали-перед тем.) Тем не менее, секвенировали-перед тем заказ в значительной степени то, что вы ожидаете. Заявления упорядочены в порядке, указанном в программе. Раздел 1.9, пункт 14: «Каждое вычисление значения и побочный эффект, связанный с полным выражением, упорядочивается перед каждым значением
вычисление и побочный эффект, связанный со следующим полным выражением, которое будет оценено. «
Весь смысл Раздела 1.10 состоит в том, чтобы сказать, что программа, которая данных гонки бесплатно выдает такое же четко определенное значение, как если бы программа запускалась на машине с последовательно согласованной памятью и без переупорядочения компилятора. Если существует гонка данных, то программа вообще не имеет определенной семантики. Если нет гонки данных, то компилятору (или машине) разрешается переупорядочивать операции, которые не способствуют иллюзии последовательной согласованности.
Раздел 1.10, пункт 21 (стр. 14) гласит: «Программа не данных гонки бесплатно если существует пара обращений A и B из разных потоков к объекту X, по крайней мере один из этих доступов имеет побочный эффект, и ни A не происходит до B, ни B не происходит до A. В противном случае программа работает в режиме гонки данных. -свободно.
В параграфах 6-20 дано очень тщательное определение отношения «происходит до того». Ключевое определение — пункт 12:
«Оценка А происходит раньше оценка B, если:
Так что, если приобретение последовательность перед (в том же потоке) в значительной степени любое другое утверждение, тогда должно быть, что приобретение происходит перед этим утверждением. (Включая, если этот оператор выполняет запись.)
Аналогично: если в значительной степени любое утверждение последовательность перед (в том же потоке) релиз, то это заявление должно произойти до выпуска. (Включая, если этот оператор просто вычисляет значение (читай).)
Причина в том, что компилятор является разрешено перемещать другие вычисления из после выпуска в до выпуска (или из до приобретения в после приобретения) из-за того факта, что эти операции специально не Имейте межпотоковый процесс, предшествующий отношениям (потому что они находятся вне критического раздела). Если они участвуют в гонке, семантика не определена, и если они не участвуют в гонке (потому что они не являются общими), то вы не можете точно сказать, когда они произошли в отношении синхронизации.
Это очень длинный способ сказать: определения приобретения и выпуска cppreference.com совершенно неверны. В вашем примере программы нет условия гонки данных, и PANIC не может возникнуть.
Других решений пока нет …