Zmienna ulotna – zmienna lub obiekt, które mogą zostać zmienione "z zewnątrz" — niezależnie od kodu programu, w którym się znajdują.
Pomiędzy różnymi odczytami, wartości zmiennej mogą być różne, nawet jeśli nie były zmodyfikowane w kodzie. Zastosowanie volatile powstrzymuje kompilator optymalizujący przed pomijaniem zapisów do pamięci lub w wypadku kolejnych odczytów czy zapisów zmiennej przed zastąpieniem jej w skompilowanym kodzie przez stałą. Zmienne ulotne pojawiają się przede wszystkim w dostępie do sprzętu, gdzie korzystanie z pamięci jest wykorzystywane do komunikacji pomiędzy urządzeniami oraz w środowisku wielowątkowym, w którym różne wątki mogą korzystać z tej samej zmiennej.
Pomimo bycia powszechnym słowem kluczowym dokładne zachowanie volatile różni się pomiędzy językami programowania. W C i C++ jest modyfikatorem do typu podobnie jak słowo kluczowe const i nie sprawdza się w większości szablonów programów wielowątkowych, dlatego jego zastosowanie jest odradzane. W językach C# i Java jest przeznaczone specjalnie do wielowątkowości — charakteryzuje zmienną i oznacza, że obiekt, z którym jest ona powiązana, może się zmienić.
C oraz C++ W C i następnie w C++ słowo volatile miało spełniać następujące założenia: dawać dostęp do urządzeń MMIO pozwalać na korzystanie ze zmiennych pomiędzy setjmp i longjmp pozwalać na używanie zmiennych sig_atomic_t w procedurach obsługi przerwań (ang. signal handlers) Operacje na zmiennych ulotnych nie są operacjami atomowymi, ani też nie ustanawiają prawidłowiej relacji (określa w jakiej względnej kolejności wykonywane są instrukcje). Jest to określone w odpowiednich standardach (C, C++, POSIX, WIN32). Zmienne ulotne nie są bezpieczne dla zdecydowanej większości dzisiejszych implementacji programów wielowątkowych, dlatego też użycie volatile jako przenośnego mechanizmu synchronizacji jest odradzane.
Przykład zastosowania w C Poniższy kod inicjuje zmienną foo na 0 i wykonuje pętlę while dopóki foo nie jest równe 255:static int foo;
void var(void) { foo = 0;
while (foo != 255){ /* ... */
}Kompilator optymalizujący zauważy, że żaden inny kod nie może zmienić wartości foo, dlatego założy, że pozostanie ona równa 0. Zamieni wtedy warunek wewnątrz pętli na:static int foo;
void var(void) { foo = 0;
while (true){ /* ... */
}Problem pojawia się, gdy foo reprezentuje na przykład pewną lokację w pamięci, która może być zmieniona przez inne elementy systemu w dowolnej chwili (np. rejestr urządzeń lub CPU). Powyższy kod nigdy nie wykryłby takiej zmiany — bez volatile kompilator zakłada, że tylko bieżący program może zmienić wartość foo. Aby zapobiec intruzyjnej optymalizacji kompilatora należy zastosować volatile w następujący sposób:static volatile int foo;
void var(void) { foo = 0;
while (foo != 255){ /* ... */
}W powyższym wypadku kompilator wygeneruje kod, który za każdym razem, gdy będziemy próbowali odczytać wartość zmiennej foo, załaduje jej wartość z oryginalnego miejsca w pamięci (zamiast chociażby korzystać z wartości zapisanej w pamięci cache). Mechanizm ten jest często wykorzystywany w systemach wbudowanych do pobierania danych ze sprzętowych modułów wbudowanych w mikrokontrolery (np. przetwornika analogowo-cyfrowego).
Na większości dzisiejszych platform istnieje system bariery pamięci (od C++11), który powinien być wykorzystywany zamiast mechanizmu volatile, ponieważ pozwala kompilatorowi na lepszą optymalizację i zapewnia poprawne zachowanie podczas operacji wielowątkowych; zarówno C (przed C11), jak i C++ (przed C++11) zakładają modelu wielowątkowego dostępu do pamięci, dlatego zachowanie volatile może nie być deterministyczne pomiędzy kompilatorami/procesorami/systemami operacyjnymi.
C++11 Według standardu C++11 ISO słowo kluczowe volatile jest przeznaczone jedynie dla dostępu sprzętowego i nie należy go używać do komunikacji między wątkami — do tego biblioteka STL przeznaczyła szablony std::atomic<T>.
Java Język Java również posiada słowo kluczowe volatile, lecz ma ona nieco inną specyfikację: We wszystkich wersjach Javy istnieje globalna kolejność odczytów i zapisów do zmiennej z volatile. Dzięki temu każdy wątek mający do niej dostęp odczyta jej obecną wartość przed kontynuacją, zamiast (potencjalnego) wykorzystania zmiennej przechowywanej w pamięci podręcznej. Od Javy 5 zmienne z modyfikatorem volatile zachowują relację . Używanie volatile może być szybsze niż blokowanie, ale może też nie działać w niektórych wypadkach.
Przypisy
Kategoria:Zmienna (informatyka)