W C# istnieje dość mało popularny atrybut ThreadStaticAttribute. Do czego on służy? Zacznijmy od przykładu:
internal class Program { [ThreadStatic] private static int _value; private static void Main(string[] args) { Thread thread1 = new Thread(PrintValue); Thread thread2 = new Thread(PrintValue); thread1.Start(4); thread1.Join(); thread2.Start(); } static private void PrintValue(object args) { if(args!=null) _value = (int) args; Console.WriteLine(_value); } }
Co się pojawi na wyjściu? Bez atrybutu ThreadStatic byłoby to (4,4). Atrybut jednak mówi, że pole statyczne będzie unikalne dla każdego wątku. Czyli Thread1 będzie miał własną kopię oraz tak samo Thread2. Statyczne pola oznaczone tym atrybutem nie są współdzielone przez wątki. W powyższym przykładzie, pierwszy wątek ustawi wartość na 4 a drugi z kolei będzie miał swoją kopie i wyświetli wartość domyślną czyli 0.
Należy zaznaczyć, że pole ThreadStatic powoduje pewien overhead i czytanie takiej wartości jest dużo wolniejsze (nawet kilkadziesiąt razy). Kiedy używać tego? Szczerze mówiąc rzadko, tak samo jak rzadko należy używać klas statycznych. Jeśli mamy jednak pole statyczne, które jest modyfikowane przez różne wątki, wtedy aby uniknąć kosztownych blokad, warto oznaczyć je ThreadStatic.
Ważna uwaga: nie należy inicjalizować pól statycznych ThreadStatic wartością początkową. Dlaczego? Pomyślmy kiedy wartość początkowa jest wstawiana. Na etapie kompilacji? Nie… Ma to miejsce w momencie pierwszego dostępu do pola a konkretnie kiedy zostanie wywołany konstruktor statyczny. Niestety wartość domyślna będzie widziana tylko dla pierwszego wątku, który chciał uzyskać dostęp:
internal class Program { [ThreadStatic] private static int _value = 54; private static void Main(string[] args) { Console.WriteLine(_value); Thread thread1 = new Thread(PrintValue); Thread thread2 = new Thread(PrintValue); thread1.Start(); thread1.Join(); thread2.Start(); } static private void PrintValue(object args) { Console.WriteLine(_value); } }
Powyższy kod wyświetli wartości 54,0,0. Główny wątek wywoła inicjalizację wartością 54 a następne niestety już tego nie uczynią. Jedynie możemy mieć pewność, że wartość w nowych wątkach będzie wartością domyślną ( dla liczb zero a dla obiektów NULL).
Fajny artykuł, nie wiedziałem o tym atrybucie!
Znalazłem mały błąd: zamiast “Pomyślny” powinno być chyba “Pomyślmy” 🙂
Pozdrawiam
Dzieki, poprawione:)