Bariera pamięci
Bariera pamięci – punkt w programie, w którym wymusza się na systemie komputerowym zsynchronizowanie zawartości pamięci, tj. uszeregowanie wszystkich zapisów i odczytów. Jest to potrzebne, ponieważ niektóre mikroprocesory w celu uzyskania lepszej wydajności nie uaktualniają od razu zawartości pamięci głównej lub podręcznej, np. zastępując powtarzające się zapisy pod ten sam adres w jedno, opóźnione, odwołanie do pamięci.
Bariera pamięci jest realizowana przez specjalną instrukcję procesora.
Bez zastosowania bariery może dojść do sytuacji, gdy wątki lub procesy odwołując się do tego samego adresu pamięci odczytają zupełnie inne dane, co będzie błędem.
Specyficzne realizacje niskopoziomowe
Do realizacji bariery pamięci stosuje się specjalne instrukcje procesora, które wymuszają zsynchronizowanie zawartości wybranego fragmentu pamięci podręcznej z główną. Szczegółowe zachowanie podczas obsługi bariery zależy od architektury procesora.
W niektórych architekturach, na przykład w procesorach x86, dostępne są instrukcje, które zapewniają „pełną barierę”. Pełna bariera oznacza, że wszystkie instrukcje czytające lub zapisujące umieszczone przed nią są w pełni zrealizowane przed dowolnymi instrukcjami odczytującymi lub zapisującymi za nią. W innych architekturach, na przykład Itanium, odróżnia się również dwa rodzaje barier częściowych tj. „zajęcie” (ang. acquire) dla odczytu i „zwolnienie” (ang. release) dla zapisu, które dotyczą widoczności danych w operacjach odczyt-po-zapisie. Bariera typu „zajmij” zapewnia, że instrukcje następujące po niej zostaną wykonane dopiero po zsynchronizowaniu pamięci podręcznej z główną. Bariera typu „zwolnij” zapewnia, że instrukcje występujące przed nią zostaną całkowicie zrealizowane, po czym nastąpi zsynchronizowanie pamięci podręcznej z główną[1][2].
Języki wysokiego poziomu
C++
Początkowo standard języka C++ nie definiował metody na generowanie bariery pamięci. W celu ich realizacji należało odwołać się do specyficznych rozszerzeń kompilatora[3][4] lub funkcji systemu operacyjnego. Wraz z pojawieniem się C++11, pojawiły się standardowe mechanizmy[5] ułatwiające korzystanie z wątków.
Począwszy od wersji kompilatora Microsoft Visual Studio 2005 słowo kluczowe volatile
służy do definiowania barier częściowych[6]. Dzięki temu odczyt danych z obiektu zadeklarowanego jako volatile tworzy barierę typu „zajmij” (ang. acquire), a zapis barierę typu „zwolnij” (ang. release). Pozwala to na tworzenie sekcji krytycznych w programowaniu wielowątkowym.
W przypadku kompilatora GCC, stosowanie słowa kluczowego volatile
jest niewystarczające dla uzyskania bariery pamięci i należy dodatkowo stosować rozszerzenie języka do jej jawnego zadeklarowania w postaci[7]:
asm volatile ("" : : : "memory");
C#
Wśród klas biblioteki standardowej znajdują się funkcje, które implementują pełną barierę pamięci[8]:
- wszystkie metody w klasie
Interlocked
- niektóre metody w klasie
Thread
:Thread.MemoryBarrier
,Thread.VolatileRead
,Thread.VolatileWrite
- wbudowany w język mechanizm tworzenia sekcji krytycznych, za pomocą słowa kluczowego
lock
bądź odpowiadających mu metodMonitor.Enter
iMonitor.Exit
.
Inną metodą definiowania częściowych barier jest stosowanie słowa kluczowego volatile
w deklaracji zmiennej. Informuje ono kompilator, żeby wygenerować barierę typu „zajmij” (ang. acquire) przy odczycie zmiennej, oraz barierę typu „zwolnij” (ang. release) podczas zapisu zmiennej[8].
Java
Jawne deklarowanie bariery pamięci w Javie nie jest dostępne. Wirtualna maszyna Javy decyduje o ich stosowaniu automatycznie w miejscach, gdzie jest to wymagane przez współbieżność i semantykę języka. Dotyczy to zwłaszcza stosowania słów kluczowych volatile
i synchronized
. Ich generowanie jest silnie zależne od platformy sprzętowej na której wirtualna maszyna jest uruchomiona[9].
Zobacz też
Przypisy
- ↑ MemoryConsistency, [w:] IA64wiki [online], 10 grudnia 2009 [dostęp 2012-11-22] [zarchiwizowane z adresu 2012-03-21] (ang.).
- ↑ Memory ordering, barriers, acquire and release semantics, 29 czerwca 2008 [dostęp 2012-11-22] [zarchiwizowane z adresu 2015-04-19] (ang.).
- ↑ _ReadWriteBarrier, [w:] MSDN [online] [dostęp 2012-11-24] (ang.).
- ↑ Atomic Builtins – Using the GNU Compiler Collection (GCC) [dostęp 2012-11-24] (ang.).
- ↑ Atomic operations library, 2 listopada 2012 [dostęp 2012-11-24] (ang.).
- ↑ volatile (C++), [w:] MSDN [online] [dostęp 2012-11-23] .
- ↑ Volatiles – Using the GNU Compiler Collection (GCC) [dostęp 2012-11-24] (ang.).
- ↑ a b Albahari 2011 ↓.
- ↑ Dennis Byrne , Memory Barriers and JVM Concurrency, 8 marca 2010 [dostęp 2012-11-23] (ang.).
Bibliografia
- Joseph Albahari , Threading in C#. Part 4: Advanced Threading, 27 kwietnia 2011 [dostęp 2012-11-23] (ang.).
- Maurice Herlihy , Nir Shavit , Sztuka programowania wieloprocesorowego, Warszawa: PWN, 2010, ISBN 978-83-01-16146-0 .
Linki zewnętrzne
- Jeff Preshing , Memory Ordering at Compile Time, 25 czerwca 2012 [dostęp 2012-11-24] (ang.).