2014-12-02 16 views
14

Niepowodzenie synchronizacji stylu Dekkera zwykle tłumaczy się przez zmianę kolejności instrukcji. To znaczy, jeśli piszemyDlaczego nie ma wystarczającej ilości C++ 11 fence_release do synchronizacji Dekkera?

atomic_int X; 
atomic_int Y; 
int r1, r2; 
static void t1() { 
    X.store(1, std::memory_order_relaxed) 
    r1 = Y.load(std::memory_order_relaxed); 
} 
static void t2() { 
    Y.store(1, std::memory_order_relaxed) 
    r2 = X.load(std::memory_order_relaxed); 
} 

Następnie ładunki mogą być zreorganizowane ze sklepów, co prowadzi do r1==r2==0.

Spodziewałem ogrodzenie acquire_release aby zapobiec tego rodzaju zmiany kolejności:

static void t1() { 
    X.store(1, std::memory_order_relaxed); 
    atomic_thread_fence(std::memory_order_acq_rel); 
    r1 = Y.load(std::memory_order_relaxed); 
} 
static void t2() { 
    Y.store(1, std::memory_order_relaxed); 
    atomic_thread_fence(std::memory_order_acq_rel); 
    r2 = X.load(std::memory_order_relaxed); 
} 

Ładunek nie może zostać przeniesiony nad ogrodzeniem i magazyn nie może być przeniesiona pod ogrodzeniem, a więc zły wynik należy zapobiegać .

Jednak eksperymenty pokazują, że nadal może występować r1==r2==0. Czy jest to uzasadnione na nowo? Gdzie jest wada mojego rozumowania?

Odpowiedz

8

Jak rozumieją (głównie z lektury Jeff Preshings blog) dana atomic_thread_fence(std::memory_order_acq_rel) zapobiega reorderings wyjątkiem StoreLoad, to wciąż pozwala zmienić kolejność Store z kolejnym Load. Jednak jest to właśnie zmiana kolejności, której należy zapobiegać w twoim przykładzie.

Dokładniej An atomic_thread_fence(std::memory_order_acquire) uniemożliwia zmianę kolejności poprzednich Load z późniejszymi Store i wprowadzonych Load, to znaczy zapobiega LoadLoad i LoadStore reorderings przez ogrodzenie.

atomic_thread_fence(std::memory_order_release) uniemożliwia zmianę kolejności każdego kolejnego Store któregokolwiek z poprzedzających Store i dowolnego z poprzednich Load, to zapobiega LoadStore i StoreStore reorderings przez ogrodzenie.

An atomic_thread_fence(std::memory_order_acq_rel) następnie zapobiega związku, to znaczy zapobiega LoadLoad, LoadStore i StoreStore, co oznacza, że ​​tylko StoreLoad nadal może się zdarzyć.

4

memory_order_acq_rel faktycznie zachowuje się tak samo, jak nabywanie i odblokowywanie ogrodzenia w tym samym miejscu. Problem polega jednak na tym, że nie zapobiegają one wszelkim ewentualnym zmianom kolejności, uniemożliwiają kolejność ładunków lub uprzednich zapasów po zmianie ogrodzenia. Tak więc, wcześniejsze obciążenia i kolejne sklepy mogą nadal przechodzić przez ogrodzenie.

W synchronizacji Dekkera ważne jest, aby zapobiec np. ładunek nie został ponownie zamówiony przed magazynem w innym wątku, tj. przed ogrodzeniem. Teraz rozwiąż swoje pętle w miejscu, w którym pojawia się ta synchronizacja, a otrzymasz informację, że obciążenie z poprzedniej iteracji może spaść przez ogrodzenie w bieżącej iteracji.

memory_order_seq_cst działa dobrze dla synchronizacji Dekkera, ponieważ zapobiega ponownemu porządkowaniu w tym punkcie. Na przykład używa tego algorytmu Dekkera i mfence do kradzieży pracy.

Dla lepszego zrozumienia, zobacz wspaniałą animację z wykładu Herba Suttera "Atomic<> weapons 1/2", o 0:43.