Вы правы в том, что барьер памяти не может гарантировать, что чтение видит актуальные значения.Что он делает, так это устанавливает порядок между операциями, как в одном потоке, так и между потоками.
Например, если поток A выполняет серию хранилищ, а затем выполняет барьер освобождения перед окончательным хранением длярасположение флага, и поток B считывает из местоположения флага, а затем выполняет барьер получения перед чтением других значений, тогда другие переменные будут иметь значения, сохраненные потоком A:
// initially x=y=z=flag=0
// thread A
x=1;
y=2;
z=3;
release_barrier();
flag=1;
// thread B
while(flag==0) ; // loop until flag is 1
acquire_barrier();
assert(x==1); // asserts will not fire
assert(y==2);
assert(z==3);
Конечно, вам нужноубедитесь, что загрузка и сохранение в flag
являются атомарными (что простые инструкции загрузки и сохранения находятся на большинстве распространенных процессоров, при условии, что переменные соответствующим образом выровнены).Без цикла while в потоке B поток B вполне может прочитать устаревшее значение (0) для flag
, и, таким образом, вы не можете гарантировать любое из значений, считанных для других переменных.
Таким образом, можно использовать заборыдля обеспечения синхронизации в алгоритме Деккера.
Вот пример реализации на C ++ (с использованием атомарных переменных C ++ 0x):
std::atomic<bool> flag0(false),flag1(false);
std::atomic<int> turn(0);
void p0()
{
flag0.store(true,std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
while (flag1.load(std::memory_order_relaxed))
{
if (turn.load(std::memory_order_relaxed) != 0)
{
flag0.store(false,std::memory_order_relaxed);
while (turn.load(std::memory_order_relaxed) != 0)
{
}
flag0.store(true,std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
}
}
std::atomic_thread_fence(std::memory_order_acquire);
// critical section
turn.store(1,std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_release);
flag0.store(false,std::memory_order_relaxed);
}
void p1()
{
flag1.store(true,std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
while (flag0.load(std::memory_order_relaxed))
{
if (turn.load(std::memory_order_relaxed) != 1)
{
flag1.store(false,std::memory_order_relaxed);
while (turn.load(std::memory_order_relaxed) != 1)
{
}
flag1.store(true,std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_seq_cst);
}
}
std::atomic_thread_fence(std::memory_order_acquire);
// critical section
turn.store(0,std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_release);
flag1.store(false,std::memory_order_relaxed);
}
Полный анализ см. в моей записи блога по адресу http://www.justsoftwaresolutions.co.uk/threading/implementing_dekkers_algorithm_with_fences.html