Często można usłyszeć, że przypisania są zawsze bezpieczne w wielowątkowości i powinniśmy martwić się np. inkrementacją. Jest to prawda dla Int32 ale dla long już nie zawsze. Przykład:
internal class Program { private static long _x = 0; private static void Main(string[] args) { Task.Factory.StartNew(Task1); Task.Factory.StartNew(Task2); Thread.Sleep(5000); } private static void Task2() { while (true) { Console.WriteLine(_x); } } private static void Task1() { while (true) { if (_x == 0) _x = long.MaxValue; else _x = 0; } } }
W kodzie mamy zwykle przypisania. Spodziewalibyśmy się, że na ekranie będzie zawsze 0 albo long.MaxValue – w końcu żadnej innej wartości nie ustawiamy tutaj. Jeśli uruchomimy powyższy kod w trybie x86 otrzymamy:
Dlaczego na ekranie mamy tak różne wartości? Operacje na Int64 są bezpieczne wyłącznie, jeśli kod jest skompilowany i wykonany na procesor x64. Dla x86 są one rozdzielane na dwie operacje, które ustawiają najpierw 32 bitów i potem w drugiej operacji kolejne bity. Kompilując ten sam kod pod x64 dostaniemy oczekiwane rezultaty:
Projektując kod należy mieć to na uwadze bo np. identyfikatory często są typu long i nie wszyscy zdają sobie sprawę, że taki kod może być ryzykowny. Powyższe zachowanie nazywa się “torn reads” ponieważ czytamy tak naprawdę tylko częściowe wyniki.
Dzięki za wpis! Odkrył bym to zapewne dopiero jak bym się nadział.
Czy istnieje zatem jakieś zabezpieczenie jak już musimy użyć Int64 na x86?
Mozna uzyc Interlocked.Read:
http://msdn.microsoft.com/en-us/library/system.threading.interlocked.read.aspx