Blokowanie
Wątek zablokowany, to taki, którego wykonywanie z jakiegoś powodu zostało wstrzymane, na przykład poprzez operację Sleep bądź Join względem innego wątku. Wątek w stanie zablokowania oddaje swoją część procesora do momentu wznowienia. Odblokować wątek można na cztery sposoby:
- warunek wznowienia zostaje spełniony
- przekroczony timeout (jeżeli został ustawiony)
- przerwanie przez Thread.Interrupt
- wykonanie instrukcji Thread.Abort
while (!proceed) Thread.Sleep (10);
Stan wątku jest dostępny w każdej chwili za pomocą property ThreadState. Możliwe stany wątków przedstawione są na diagramie poniżej.
Wątki zostają zablokowane na przykład, gdy użyjemy instrukcji lock. Instrukcja ta jest skrótem składniowym odpowiadającym instrukcjom Monitor.Enter oraz Monitor.Exit. Obiekt na którym wykonywana jest blokada musi dziedziczyć po typie referencyjnym.
static readonly object _locker = new object(); static int _x; static void Increment() { lock (_locker) _x++; } static void Decrement() { lock (_locker) _x--; } public static void Run() { for (int i = 0; i < 5; i++) { Task.Factory.StartNew(Increment); Task.Factory.StartNew(Decrement); } }
Lock jest instrukcją atomową, chyba że w jego wnętrzu zostanie rzucony wyjątek.Podobny efekt można uzyskać stosując Mutex. Konstrukcja ta mimo iż jest kilkadziesiąt razy wolniejsza, ma jedną ważną przewagę nad lockiem - można za jej pomocą wykonywać blokady pomiędzy procesami.
static void RunMutex() { using (var mutex = new Mutex(false, "Sample Mutex String")) { if (!mutex.WaitOne(TimeSpan.FromSeconds(3), false)) { Console.WriteLine("Another app instance is running. Bye!"); return; } RunProgram(); } } static void RunProgram() { Console.WriteLine("Running. Press Enter to exit"); Console.ReadLine(); }
Nazwa Mutex-a pozwoli mu blokować procesy w obrębie całego komputera. Jeżeli powyższy program zostanie uruchomiony w dwóch instancjach, to druga instancja poczeka 3 sekundy i zakończy swoją pracę, jeżeli kod pierwszej instancji nie wykona się (nie zostanie wciśnięty Enter).
Semafor
Semafor działa jak lock, z tą różnicą że jest w stanie obsłużyć więcej niż jeden wątek. Liczba wątków definiowana jest przez programistę, a każdy nadmiarowy wątek musi poczekać, aż któryś z poprzedników skończy swoje działanie.
static SemaphoreSlim _sem = new SemaphoreSlim(3); public static void Run() { for (int i = 1; i <= 5; i++) new Thread(Enter).Start(i); } static void Enter(object id) { Console.WriteLine(id + " wants to enter"); _sem.Wait(); Console.WriteLine(id + " is in!"); Thread.Sleep(1000*(int) id); Console.WriteLine(id + " is leaving"); _sem.Release(); }
SemaphoreSlim to klasa z .NET 4.0 zoptymalizowana pod kątem czasu wykonywania operacji Wait i Release.
Brak komentarzy:
Prześlij komentarz