2011-09-26 13 views
15

Co oznacza metoda AtomicXXX.lazySet (wartość) w kategoriach zaistniałych wcześniej krawędzi, wykorzystywanych w większości przypadków rozumowania JMM? Javadocs jest czysta na nim, i Sun bug 6275329 stany:AtomicXXX.lazySet (...) pod względem występowania przed krawędziami

Semantyka to, że zapis jest zagwarantowane, aby nie zostać zreorganizowane z dowolnego poprzedniego zapisu, ale może zostać zreorganizowane z kolejnych operacji (lub równoważnie, może nie być widocznym dla innych wątków), dopóki nie wystąpi inna, lotna operacja zapisu lub synchronizacji).

Ale to nie jest rozumowanie na temat krawędzi HB, więc mnie to myli. Czy to znaczy, co semantyka lazySet() nie może być wyrażona w kategoriach krawędzi HB?

UPDATE: Postaram się skonkretyzować moje pytanie. mogę użyć zwykłego pola lotnego w następujący scenariusz:

//thread 1: producer 
...fill some data structure 
myVolatileFlag = 1; 

//thread 2: consumer 
while(myVolatileFlag!=1){ 
    //spin-wait 
} 
...use data structure... 

W tym zastosowaniu scenariusza „struktury danych” w konsument jest poprawna, ponieważ lotny flaga zapisu odczytu uczynić przewagę HB, co daje gwarancję, co wszystkie zapisy do „struktura danych "przez producenta zostanie zakończona i widoczna dla konsumenta. Ale co jeśli użyję AtomicInteger.lazySet/get zamiast volatile write/read w tym scenariuszu?

//thread 1: producer 
...fill some data structure 
myAtomicFlag.lazySet(1); 

//thread 2: consumer 
while(myAtomicFlag.get()!=1){ 
    //spin-wait 
} 
...use data structure... 

czy nadal będzie poprawny? Czy nadal mogę naprawdę zobaczyć widoczność wartości struktury danych w wątku konsumenckim?

To nie jest „z powietrza” pytanie - Widziałem taką metodę w kodzie Lmax Zakłócacza dokładnie w tym scenariuszu, a ja nie rozumiem, jak udowodnić, że jest poprawne ...

+0

jeśli metoda lazySet działa jako bariera sklepu-sklepu, można z niej korzystać. Kiedy wątek2 zobaczy flagę, wszystko będzie w porządku, aby odczytać strukturę. LazySet zapewnia, że ​​zapis będzie widoczny w pewnym momencie, a wszystkie architektury pozwalają na automatyczną widoczność, tj. Jeśli coś jest napisane, będzie widoczne i ostatecznie spójne. – bestsss

Odpowiedz

10

Operacje lazySet nie powodują powstawania wcześniejszych krawędzi i dlatego nie gwarantują, że będą od razu widoczne. Jest to optymalizacja niskiego poziomu, która ma tylko kilka przypadków użycia, które są najczęściej w równoległych strukturach danych.

Przykład zerowania zbioru łączonych wskaźników listy nie ma żadnych widocznych efektów ubocznych. Wyciszanie jest preferowane, więc jeśli węzły na liście są w różnych generacjach, nie wymusza to wykonania kosztownej kolekcji w celu odrzucenia łańcucha łącza.Użycie lazySet zachowuje higieniczną semantykę bez ponoszenia nadmiernego naprężenia zapisu.

Innym przykładem jest użycie zmiennych pól chronionych przez blokadę, na przykład ConcurrentHashMap. Pola są zmienne, aby umożliwić odczyty bez blokowania, ale zapisy muszą być wykonywane pod blokadą, aby zapewnić ścisłą spójność. Ponieważ blokada gwarantuje zachowanie przed uruchomieniem, optymalizacja polega na użyciu lazySet podczas zapisywania do pól i płukania wszystkich aktualizacji podczas odblokowywania. Pomaga to skrócić krytyczną sekcję, unikając niepotrzebnych straganów i ruchu autobusowego.

Jeśli napiszesz współbieżną strukturę danych, to lazySet to dobra sztuczka, o której warto pamiętać. Jest to optymalizacja na niskim poziomie, więc warto ją rozważyć tylko w przypadku dostrajania wydajności.

+0

Dziękuję za wyjaśnienia. Czy możesz również spojrzeć na aktualizację i skomentować to? – BegemoT

+0

Jako zestaw, a nie cas, jest on bezpieczny tylko wtedy, gdy nie będzie nadpisywać ważnych danych, gdy stanie się widoczny. Wielu producentów rywalizowało o slot, więc nie jest to bezpieczne. Byłoby bezpiecznie, gdyby pojedynczy konsument opróżnił bufor, opóźniając widoczność wolnego gniazda. Grałem z tym kilka miesięcy temu, zobacz mój [bufor pierścieniowy] (http://code.google.com/p/concurrentlinkedhashmap/source/browse/trunk/src/main/java/com/googlecode/concurrentlinkedhashmap/RingBuffer .java? spec = svn754 & r = 749) kod. –

+0

Jest to pojedynczy producent/pojedynczy konsument. Ale nadal nie rozumiem, dlaczego jest on bezpieczny - w jaki sposób mogę udowodnić, co 1) pisze, zanim sekwencja stronnicza (seq) jest zakończona i _visible_ do wątku 2 po sequence.get() == seq? a co 2) lazySet (seq) jest rzeczywiście napisany w pewnym skończonym czasie: docs mówi "ostatecznie" - co może oznaczać "w sekundę", "w godzinie", a nawet "nigdy". Jeśli spojrzeć na lazySet jako nieulotnego zapisu, wygląda na to, że może być swobodnie opóźniany na zawsze przez kompilator lub procesor ... – BegemoT

3

podstawie Javadoc niebezpiecznych (the putOrderedInt jest stosowany w AtomicInteger.lazySet)

/** 
* Version of {@link #putObjectVolatile(Object, long, Object)} 
* that does not guarantee immediate visibility of the store to 
* other threads. This method is generally only useful if the 
* underlying field is a Java volatile (or if an array cell, one 
* that is otherwise only accessed using volatile accesses). 
*/ 
public native void putOrderedObject(Object o, long offset, Object x); 

/** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)} */ 
public native void putOrderedInt(Object o, long offset, int x); 

Pola oporowe w klasach AtomicXXX są niestabilne. Wydaje się, że LazySet zapisuje do tych pól tak, jakby nie były one niestabilne, co usuwałoby oczekiwane wcześniej brzegi, których się spodziewasz. Jak wspomniano w twoim linku, byłoby to użyteczne, gdyby wartości zerujące kwalifikowały się do GC bez konieczności ponoszenia zmiennego zapisu.

Edit:

Ma to na celu odpowiedzieć na aktualizację.

Jeśli spojrzysz na cytat podany w linku, stracisz wszelkie gwarancje pamięci, które masz przy zmiennym zapisie.

LazySet nie zostanie zamówiony powyżej, do którego jest zapisywany, ale bez żadnej innej faktycznej synchronizacji tracisz gwarancję, że konsument zobaczy wszelkie zmiany, które zostały napisane przed nim. Całkowicie legalne jest opóźnianie zapisu myAtomicFlag i tam wszelkie wcześniejsze zapisy, dopóki nie nastąpi inna forma synchronizacji. Operacje

+0

lazySet zapewnia brak zmiany kolejności zapisu, ale nie rób tego dla odczytu, tzn. Kolejne odczyty mogą być wykonywane przed propagacją wartości. dla x86 lazySet jest po prostu instrukcją mov. W powyższym przykładzie lazySet jest w porządku, imo ... i jest to świetna wygrana. – bestsss

Powiązane problemy