Nawet na prosty 2-wątku przykład komunikacji, mam trudność wyrazić to w C11 atomowej i memory_fence stylu w celu uzyskania odpowiedniej pamięci Kolejność:C11 płot pamięć Wykorzystanie
wspólne dane:
volatile int flag, bucket;
wątek
producent:
while (true) {
int value = producer_work();
while (atomic_load_explicit(&flag, memory_order_acquire))
; // busy wait
bucket = value;
atomic_store_explicit(&flag, 1, memory_order_release);
}
konsument thre ad:
while (true) {
while (!atomic_load_explicit(&flag, memory_order_acquire))
; // busy wait
int data = bucket;
atomic_thread_fence(/* memory_order ??? */);
atomic_store_explicit(&flag, 0, memory_order_release);
consumer_work(data);
}
O ile mi zrozumieć, że powyższy kod poprawnie zamówić sklep-w-wiaderku -> flag-sklep -> Flaga obciążenia -> load-z-wiadra. Uważam jednak, że nadal istnieje warunek wyścigowy między ładowaniem z zasobnika i ponownym zapisywaniem wiadra z nowymi danymi. Aby wymusić zamówienie po przeczytaniu wiadra, domyślam się, że potrzebuję jawnego atomic_thread_fence()
między odczytem wiadra i następującą stacją atomową. Niestety, wydaje się, że nie ma argumentu, aby wymusić cokolwiek na poprzednich ładunkach, nawet na memory_order_seq_cst
.
Naprawdę zabrudzonym rozwiązaniem może być ponowne przypisanie bucket
w wątku konsumenckim z fałszywą wartością: jest to sprzeczne z pojęciem "tylko do odczytu".
W starszym C99/GCC świecie mógłbym użyć tradycyjnego __sync_synchronize()
, który moim zdaniem byłby wystarczająco silny.
Jakie byłoby ładniejsze rozwiązanie w stylu C11 do synchronizowania tej tak zwanej anty-zależności?
(Oczywiście zdaję sobie sprawę, że powinienem lepiej unikać takiego kodowanie niskopoziomowe i wykorzystywać dostępne konstrukcje wyższego poziomu, ale chciałbym zrozumieć ...)
Nie jestem programistą C++, ale (koncepcyjnie) nie jestem pewien, czy konieczne jest wywołanie 'atomic_thread_fence()'. Aktualizacja flagi ma semantykę wyzwalającą, zapobiegając przeporządkowaniu wszystkich poprzednich instrukcji sklepu (np. Do "danych"). Zapis do 'data' ma zależność od odczytu z' bucket', więc odczyt nie może zostać ponownie uporządkowany po wydaniu flagi. Jeśli konieczne jest pełne ogrodzenie, bardzo chciałbym usłyszeć dlaczego. –
Brak odpowiedzi, a więc tylko komentarz: wydaje się, że ponownie wynajduje się typ danych "atomowej_flagi" C11, który implementuje dokładnie to semantyczne, ale które ostatecznie ma bardziej bezpośrednią implementację w sprzęcie. 'atomic_flag' to jedyny atomowy typ danych, który gwarantuje, że jest wolny od blokady, więc zawsze jest to preferowane w przypadku bardziej złożonych operacji. I definitywnie nie będzie potrzebował dodatkowego ogrodzenia, aby zapewnić spójność. –
Mike S, twoja odpowiedź wydaje mi się atrakcyjna, ale ... Myślałem, że wspomnienia z pamięci to zapewnienie rzeczy w podsystemie pamięci, wpływając na ld/st ops. W powyższym przykładzie "dane" prawdopodobnie staną się zmienną rejestrową, więc jej przydział nie tworzy sklepu op. To by tylko pozostawiło ładunek z łyżki do synchronizacji pamięci? (dla którego nie ma kolejnej kolejności pamięci C11?) –