Нужна помощь в понимании Boost.Atomic модели памяти `memory_order_release` Пример

Я просматривал документацию по boost Atomic и наткнулся на следующий пример:

atomic<int> a(0);

thread1:
  ... /* A */
  a.fetch_add(1, memory_order_release);

thread2:
  int tmp = a.load(memory_order_acquire);
  if (tmp == 1) 
  {
    ... /* B */
  } 
  else 
  {
    ... /* C */
  }

Я все еще немного запутался в отношении memory_order_release и memory_order_acquire. В документации они описаны как:

memory_order_release

Выполните операцию выпуска. Неформально говоря, предотвращает переупорядочение всех предыдущих операций с памятью после этого момента.

memory_order_acquire

Выполните операцию получения. Неформально говоря, предотвращает переупорядочивание последующих операций с памятью до этого момента.

Даже с этими примерами я все еще немного сбит с толку. Я был бы признателен, если бы кто-нибудь мог объяснить, что означают приведенные выше определения, а также как могут возникать конфликты между A и C?


person MistyD    schedule 03.11.2013    source источник


Ответы (1)


Определения означают, что ни одна из операций с памятью в A не может быть переупорядочена после сохранения в a (определение release в вопросе). И что ни одна из операций в B или C не может предшествовать загрузке a в другом потоке (определение получить в вопросе).

Теперь в примере предполагается, что нет другого кода, который работает с a или, по крайней мере, записывает в a. Если выполняется B, это означает, что a имел значение 1, когда произошла загрузка в tmp. Чтобы a имел значение 1, fetch_add должен был быть выполнен раньше (fetch_add синхронизируется с load), что означает, что код в A завершил выполнение. Если этот конкретный путь кода обнаружен, порядок операций следующий:

A
a.fetch_add(1,memory_order_release);
int tmp = a.load(memory_order_acquire);
B

Другой вариант состоит в том, что когда происходит load, значение по-прежнему равно 0. Это означает, что первый поток не смог выполнить fetch_add, но нет никаких гарантий относительно того, где он мог бы выполняться. К тому времени, когда поток 2 выполнит C, A может все еще выполняться (или он может даже не начаться, или он может быть завершен).

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

person David Rodríguez - dribeas    schedule 03.11.2013
comment
Спасибо за ответ, это многое объясняет. Еще один вопрос, в начале поста вы указали, что The definitions mean that none of the memory operation in A can be reordered after the store to a. Не могли бы вы объяснить, что здесь означает переупорядочение, что означает, что операции с памятью не могут быть переупорядочены? - person MistyD; 03.11.2013
comment
@MistyD: если последняя строка A содержала обновление переменной b, memory_order_release гарантирует, что ни компилятор, ни CPU не переупорядочат запись в b, чтобы она произошла после обновления до a. Без этой гарантии может возникнуть состояние гонки, если B обращается к b - person David Rodríguez - dribeas; 04.11.2013