Выбрать и добавить порядок - PullRequest
0 голосов
/ 12 октября 2018

Я работаю над заменой системы выделения для "стабильных указателей" в системе времени выполнения , и я сталкиваюсь с ограничениями моего понимания параллельного программирования.

Предположим, переменная содержит 0. Поток A использует __atomic_fetch_and_add для увеличения переменной и каким-то образом уведомляет поток B.В ответ поток B использует __atomic_fetch_and_add, чтобы уменьшить ту же самую переменную, возвращая ее к 0. Таким образом, кажется, что переменная должна идти от 0 до 1 и обратно.Гарантируется ли, что другой поток C не увидит сложения, выполненные в обратном порядке, чтобы перейти от 0 к -1 и обратно?

1 Ответ

0 голосов
/ 29 октября 2018

Я просто перечитал этот вопрос, добавив некоторые дополнительные пояснения, и понял, что предположил C11, в то время как ваш вопрос, похоже, использует встроенные компиляторы.С этой точки зрения, если все ваше memorder использование __ATOMIC_SEQ_CST, ни в одном случае вы не сможете наблюдать значение -1 по тем же причинам, которые я подробно описал ниже (из C11).

TL;ДР: Это зависит от обстоятельств, но вы должны были бы действительно выстрелить себе в ногу, чтобы не гарантировать такое поведение.Ниже следует объяснение того, почему это могло произойти, как это могло бы произойти, и почему вы вряд ли столкнетесь с этим.

Атомные операции гарантированноиметь глобальный порядок, но этот глобальный общий порядок не определен.Из проекта C11, §5.1.2.4p7:

Все модификации конкретного атомного объекта M происходят в некотором конкретном общем порядке, называемом порядком модификации из M .

Это определение модификаций M возможно, что общий порядок, наблюдаемый некоторым другим потоком, равен A / B, ноB / A также допускается.Это действительно приведет к тому, что внешний наблюдатель заметит, что значение изменяется между -1 и 0 (при условии, что тип атома со знаком).

Для решения этой проблемы стандарт определяет операции синхронизации (из пункта 5 того же раздела):

A операция синхронизации в одной или нескольких ячейках памяти является либо операцией получения , либо выпуском операция , либо операция получения и освобождения , либо операция потребление .

Позже, есть некоторые утомительные для чтения определенияза то, как эти операции сочетаются для введения зависимостей, которые в конечном итоге приводят к упорядочению «происходит до».Я опущу их;§5.1.2.4p14-22 описывают наблюдаемость побочных эффектов на некотором объекте и то, как на это влияют зависимости;§7.17.3 описывает API для управления этими зависимостями.

Не обсуждая эти разделы, можно надеяться, что достаточно сказать, что они позволяют наблюдателю увидеть описанный «противоположный порядок».В этой ситуации вы можете оказаться в ситуации, когда вы используете atomic_fetch_add_explicit с аргументом memory_order_relaxed, а ваша нагрузка реализована как atomic_load_explicit с такими же требованиями к упорядочению памяти.В этой ситуации отношение «произойдет до» не определено, и системе разрешено разрешать потоку C наблюдать изменения в любом порядке.

Вряд ли это будет тем, что вы на самом деле делаете.С одной стороны, это намного больше печатать.Во-вторых, наименование и использование API действительно предполагает, что вы должны знать, что вы делаете, если хотите его использовать.Это то, что я имею в виду, говоря, что вам действительно нужно стрелять себе в ногу: вам не рекомендуется делать подобные вещи по умолчанию.

Если вы реализовали это чисто с atomic_fetch_add, atomic_fetch_sub и atomic_load (как вы, скорее всего, сделаете), с вами все будет в порядке;стандарт в §7.17.1p5 гласит:

Функции, не заканчивающиеся на _explicit , имеют ту же семантику, что и соответствующая функция _explicit с memory_order_seq_cst для аргумента memory_order .

Стандарт гарантирует, что этот порядок будет нести зависимости данных, так что запись из потока A рассматривается как "произойдет до" записииз потока B. Поэтому наблюдателю C с его собственными последовательными требованиями к упорядочению памяти гарантируется, что операции будут чередоваться в порядке, описанном как задумано.

Это все говорит: если вы можете использовать C11, просто используйте ++, -- и =, и все будет в порядке.В соответствии с §6.5.16.2p3, операции += и -= над атомарными типами определены так, чтобы вести себя так, как будто они используют store с memory_order_seq_cst.Согласно §6.5.3p2 операторы ++ и -- аналогичны эквивалентным выражениям x+=1 и x-=1.Простое назначение (§6.5.16.2) указывает, что LHS или RHS могут быть атомарными типами, но не определяет порядок памяти. Дженс Гастед говорит , что операции с _Atomic -квалифицированными объектами гарантированно будут иметь последовательную согласованность.Я могу предугадать это только из сноски 113, а сноски не являются нормативными.Но я не думаю, что это имеет значение: если все записи согласованы, при любом чтении должно наблюдаться действительное предыдущее состояние из этого общего порядка, который никогда не содержит -1.

...