Chciałbym napisać mały program, który wizualnie ilustruje zachowanie volatile
słowa kluczowego. Idealnie, powinien to być program, który wykonuje równoczesny dostęp do nieulotnego pola statycznego i który z tego powodu uzyskuje nieprawidłowe zachowanie.
Dodanie słowa kluczowego volatile w tym samym programie powinno rozwiązać problem.
To coś, czego nie udało mi się osiągnąć. Nawet próbując kilka razy, włączając optymalizację itp., Zawsze otrzymuję poprawne zachowanie bez słowa kluczowego „volatile”.
Masz jakiś pomysł na ten temat? Czy wiesz, jak zasymulować taki problem w prostej aplikacji demonstracyjnej? Czy to zależy od sprzętu?
Tak, zależy to od sprzętu (jest mało prawdopodobne, aby problem wystąpił bez wielu procesorów), ale jest również zależny od implementacji. Specyfikacje modelu pamięci w specyfikacji CLR pozwalają na rzeczy, których implementacja CLR firmy Microsoft niekoniecznie robi.
źródło
Tak naprawdę nie chodzi o to, że wystąpi błąd, gdy słowo kluczowe „volatile” nie zostanie określone, a raczej o błąd może się zdarzyć, gdy nie zostanie ono określone. Generalnie będziesz wiedział, kiedy tak jest, lepiej niż kompilator!
Najłatwiej o tym pomyśleć, że kompilator mógłby, gdyby chciał, wstawić pewne wartości. Oznaczając wartość jako zmienną, wmawiasz sobie i kompilatorowi, że wartość może się zmienić (nawet jeśli kompilator tak nie uważa). Oznacza to, że kompilator nie powinien wprowadzać wartości w wierszu, przechowywać pamięci podręcznej ani odczytywać wartości wcześnie (w celu optymalizacji).
To zachowanie nie jest w rzeczywistości tym samym słowem kluczowym, co w C ++.
MSDN ma krótki opis tutaj . Oto być może bardziej dogłębny post na temat zmienności, atomowości i blokowania
źródło
Trudno to zademonstrować w C #, ponieważ kod jest wyabstrahowany przez maszynę wirtualną, więc na jednej implementacji tej maszyny działa poprawnie bez ulotności, podczas gdy może zawieść na innej.
Wikipedia ma jednak dobry przykład, jak zademonstrować to w C.
To samo może się zdarzyć w C #, jeśli kompilator JIT zdecyduje, że wartość zmiennej i tak nie może się zmienić, a tym samym tworzy kod maszynowy, który już jej nie sprawdza. Jeśli teraz inny wątek zmienia wartość, Twój pierwszy wątek może nadal zostać złapany w pętli.
Innym przykładem jest oczekiwanie zajęte.
Ponownie, może się to zdarzyć również w C #, ale w dużym stopniu zależy to od maszyny wirtualnej i kompilatora JIT (lub interpretera, jeśli nie ma JIT ... w teorii myślę, że MS zawsze używa kompilatora JIT, a także używa Mono jeden; ale możesz być w stanie wyłączyć go ręcznie).
źródło
Oto mój wkład w zbiorowe zrozumienie tego zachowania ... To niewiele, tylko demonstracja (oparta na demo xkipa), która pokazuje zachowanie niestabilnych wersetów nieulotną (tj. „Normalną”) wartość int, obok -z boku, w tym samym programie ... czego szukałem, gdy znalazłem ten wątek.
using System; using System.Threading; namespace VolatileTest { class VolatileTest { private volatile int _volatileInt; public void Run() { new Thread(delegate() { Thread.Sleep(500); _volatileInt = 1; }).Start(); while ( _volatileInt != 1 ) ; // Do nothing Console.WriteLine("_volatileInt="+_volatileInt); } } class NormalTest { private int _normalInt; public void Run() { new Thread(delegate() { Thread.Sleep(500); _normalInt = 1; }).Start(); // NOTE: Program hangs here in Release mode only (not Debug mode). // See: http://stackoverflow.com/questions/133270/illustrating-usage-of-the-volatile-keyword-in-c-sharp // for an explanation of why. The short answer is because the // compiler optimisation caches _normalInt on a register, so // it never re-reads the value of the _normalInt variable, so // it never sees the modified value. Ergo: while ( true )!!!! while ( _normalInt != 1 ) ; // Do nothing Console.WriteLine("_normalInt="+_normalInt); } } class Program { static void Main() { #if DEBUG Console.WriteLine("You must run this program in Release mode to reproduce the problem!"); #endif new VolatileTest().Run(); Console.WriteLine("This program will now hang!"); new NormalTest().Run(); } } }
Powyżej znajduje się kilka naprawdę doskonałych zwięzłych wyjaśnień, a także kilka świetnych odniesień. Dziękuję wszystkim za pomoc w poruszaniu się po głowie
volatile
(przynajmniej na tyle, żeby wiedzieć,volatile
gdzie był mój pierwszy instynktlock
).Pozdrawiam i dziękuję za WSZYSTKIE ryby. Keith.
PS: Byłbym bardzo zainteresowany w demo pierwotnego wniosku, który brzmiał: „Chciałbym zobaczyć jak statyczny lotnych int poprawnie zachowuje się w miarę do statycznej int misbehaves.
Próbowałem i zawiodłem to wyzwanie. (Właściwie zrezygnowałem dość szybko ;-). We wszystkim, co wypróbowałem ze statycznymi zmiennymi, zachowują się „poprawnie” niezależnie od tego, czy są niestabilne, czy nie ... i chciałbym wyjaśnić, DLACZEGO tak jest, jeśli rzeczywiście tak jest ... kompilator nie buforuje wartości zmiennych statycznych w rejestrach (tj. zamiast tego buforuje odniesienie do tego adresu sterty)?
Nie, to nie jest nowe pytanie ... to próba skierowania społeczności z powrotem na pierwotne pytanie.
źródło
Natrafiłem na następujący tekst Joe Albahari, który bardzo mi pomógł.
Wziąłem przykład z powyższego tekstu, który trochę zmieniłem, tworząc statyczne zmienne pole. Po usunięciu
volatile
słowa kluczowego program będzie blokował się na czas nieokreślony. Uruchom ten przykład w trybie wydania .class Program { public static volatile bool complete = false; private static void Main() { var t = new Thread(() => { bool toggle = false; while (!complete) toggle = !toggle; }); t.Start(); Thread.Sleep(1000); //let the other thread spin up complete = true; t.Join(); // Blocks indefinitely when you remove volatile } }
źródło