Наиболее распространенное использованиеBoost.Atomicдля реализации пользовательских протоколов синхронизации потоков: Цель состоит в координации доступа потоков к общим переменным, чтобы избежать «конфликтов». Программист должен знать, что компиляторы, процессоры и иерархии кэша могут, как правило, переупорядочивать ссылки на память по желанию. Как следствие, такая программа, как:
int x = 0, int y = 0;
thread1:
  x = 1;
  y = 1;
thread2
  if (y == 1) {
    assert(x == 1);
  }
Это может привести к провалу, поскольку нет никакой гарантии, что чтение<x>по потоку 2 «видит» запись по потоку 1.
Boost.Atomicиспользует концепцию синхронизации, основанную на отношении, происходящем до, для описания гарантий, при которых такие ситуации, как вышеупомянутая, не могут возникнуть.
В оставшейся части этого раздела будет обсуждаться— до— практическим способом вместо того, чтобы давать полностью формализованное определение. Читателю предлагается дополнительно взглянуть на обсуждение правильности некоторых изпримеров.
В качестве вводного примера, чтобы понять, как аргументация с использованиемпроисходит до того, какработает, рассмотрим две нити, синхронизирующиеся с использованием общего мутекса:
mutex m;
thread1:
  m.lock();
  ... 
  m.unlock();
thread2:
  m.lock();
  ... 
  m.unlock();
«Интуиция, основанная на блокировке», будет утверждать, что A и B не могут выполняться одновременно, поскольку кодовые пути требуют общего замка.
Однако можно прийти к такому же выводу, используя, прежде чем: Либо поток1, либо поток2 преуспеют первыми<m.lock()>. Если это поток1, то, как следствие, поток2 не может преуспеть в<m.lock()>до того, как поток1 выполнил<m.unlock()>, следовательно, A.— доВ в этом случае. По симметрии, если нить 2 преуспевает на<m.lock()>первой, мы можем заключить B.происходит доА.
Поскольку это уже исчерпывает все варианты, мы можем заключить, что либо Апроисходит — доВ, либо Впроисходит — доА должно всегда держаться. Очевидно, что они не могут указать, чтоиз двух отношений, но одного достаточно, чтобы заключить, что A и B не могут конфликтовать.
Сравните реализациюspinlock, чтобы увидеть, как концепция взаимного исключения может быть отображена на.Boost.Atomic.
 
Самый простой шаблон для координации потоковBoost.Atomicиспользует<release>и<acquire>на атомной переменной для координации: Если...
- ... нить 1 выполняет операцию А,
- ... нить 1 впоследствии записывает (или атомарно модифицирует) атомную переменную с<release>семантической,
- ... thread2 считывает (или атомарно считывает и модифицирует) значение этого значения из той же атомной переменной с<acquire>семантической и
- ...поток 2 впоследствии выполняет операцию В,
Апроисходит доВ.
Рассмотрим следующий пример
atomic<int> a(0);
thread1:
  ... 
  a.fetch_add(1, memory_order_release);
thread2:
  int tmp = a.load(memory_order_acquire);
  if (tmp == 1) {
    ... 
  } else {
    ... 
  }
В этом примере возможны два пути исполнения:
- <store>работа по потоку1 предшествует<load>по потоку2: В этом случае поток 2 будет выполнять B, а «Aпроисходит доB» выполняется, поскольку все вышеперечисленные критерии удовлетворены.
- <load>работа по потоку2 предшествует<store>по потоку1: В этом случае поток 2 будет исполнять C, но «Aпроисходит — доC» не удерживает: поток 2 не считывает значение, записанное потоком 1 через<a>.
Следовательно, A и B не могут конфликтовать, а A и Cмогутконфликтовать.
 
Ограничения упорядочивания обычно указываются вместе с доступом к атомной переменной. Однако также возможно проводить операции «забора» изолированно, в этом случае забор работает в сочетании с предыдущими (для<acquire>,<consume>или<seq_cst>операциями) или последующими (для<release>или<seq_cst>) атомными операциями.
Пример из предыдущего раздела также может быть написан следующим образом:
atomic<int> a(0);
thread1:
  ... 
  atomic_thread_fence(memory_order_release);
  a.fetch_add(1, memory_order_relaxed);
thread2:
  int tmp = a.load(memory_order_relaxed);
  if (tmp == 1) {
    atomic_thread_fence(memory_order_acquire);
    ... 
  } else {
    ... 
  }
Это обеспечивает те же гарантии заказа, что и ранее, но исключает (возможно, дорогостоящую) операцию заказа памяти в случае C.
 
Второй шаблон для координации потоков черезBoost.Atomicиспользует<release>и<consume>на атомной переменной для координации: Если...
- ... нить 1 выполняет операцию А,
- ... нить 1 впоследствии записывает (или атомарно модифицирует) атомную переменную с<release>семантической,
- ... thread2 считывает (или атомарно считывает и модифицирует) значение этого значения из той же атомной переменной с<consume>семантической и
- ...поток 2 впоследствии выполняет операцию В, котораявычислительно зависит от значения атомной переменной,
Апроисходит доВ.
Рассмотрим следующий пример
atomic<int> a(0);
complex_data_structure data[2];
thread1:
  data[1] = ...; 
  a.store(1, memory_order_release);
thread2:
  int index = a.load(memory_order_consume);
  complex_data_structure tmp = data[index]; 
В этом примере возможны два пути исполнения:
- <store>работа по потоку1 предшествует<load>по потоку2: В этом случае поток 2 будет читать<data[1]>и «Апроисходит доВ», поскольку все вышеуказанные критерии удовлетворены.
- Работа<load>по потоку2 предшествует<store>по потоку1: В этом случае поток 2 будет читать<data[0]>, а «Aпроисходит доB» не удерживает: поток 2 не считывает значение, записанное потоком 1 - 121 .
Здесьпроисходит - доотношения помогают гарантировать, что любые доступы (предположительно записывается) к<data[1]>по потоку1 происходят до того, как доступы (предположительно читается) к<data[1]>по потоку2: Не имея этой связи, поток 2 может видеть устаревшие / непоследовательные данные.
Обратите внимание, что в этом примере тот факт, что операция B вычислительно зависит от атомной переменной, поэтому ошибочна будет следующая программа:
atomic<int> a(0);
complex_data_structure data[2];
thread1:
  data[1] = ...; 
  a.store(1, memory_order_release);
thread2:
  int index = a.load(memory_order_consume);
  complex_data_structure tmp;
  if (index == 0)
    tmp = data[0];
  else
    tmp = data[1];
<consume>Наиболее часто (и наиболее безопасно) См.ограничения, используемые с указателями, сравните, например,синглтон с двойной проверкой блокировки.
 
Третий шаблон для координации потоков черезBoost.Atomicиспользует<seq_cst>для координации: Если...
- ... нить 1 выполняет операцию А,
- ... нить 1 впоследствии выполняет любую операцию с<seq_cst>,
- ... нить 1 впоследствии выполняет операцию В,
- ... резьба 2 выполняет операцию C,
- ... нить 1 впоследствии выполняет любую операцию с<seq_cst>,
- ...поток 2 впоследствии выполняет операцию D,
Апроисходит доD или Спроисходит доB.
При этом не имеет значения, работают ли нити 1 и нити 2 на одних и тех же или разных атомных переменных, или используют операцию «в одиночку»<atomic_thread_fence>.