Code review: pula wątków a maksymalna liczba wątków

W ostatnim poście wspomniałem o minimalnej liczbie wątków. Istnieje również górny próg, określający ile maksymalnie może zostać stworzonych wątków. Zbyt niski próg oraz zła architektura może spowodować bardzo trudny w znalezieniu błąd a mianowicie deadlock. Wyobraźmy sobie następującą sekwencję zdarzeń:

  1. Wątek T0 (lub główny, nie ma znaczenia) dodaje zadanie do puli.
  2. Stworzone zadanie tworzy n nowych zadań.
  3. T0 czeka aż wszystkie n zadań zostanie wykonanych (wait).

Następnie przyjmijmy, że w tych n wątkach, któryś czeka na zakończenie jakiegoś innego. Jeśli wątek numer n-2 czeka na n-1 to dojdzie do deadlock. W przypadku, gdy limit jest ustawiony na 50, a zadań do wykonania jest 70 z tym, że np. zadanie numer 60 czeka na wykonanie 61 to będziemy mieli do czynienia z zakleszczeniem. Jeśli jest to niejasne, zobrazujmy to kodem:

class Program
{
   static ManualResetEvent _blocker=new ManualResetEvent(false);

   static void Main(string[] args)
   {
       int maximal, temp;
       ThreadPool.GetMaxThreads(out maximal, out temp);
       Console.WriteLine("Max threads: {0}",maximal);

       for (int i = 0; i < maximal + 1; i++)
       {
           ThreadPool.QueueUserWorkItem(Run);
       }
       ThreadPool.QueueUserWorkItem(Signal);
       Console.ReadLine();
   }
   private static void Signal(object state)
   {
       Console.WriteLine("Signaling");
       _blocker.Set();
   }
   private static void Run(object state)
   {
       _blocker.WaitOne();
       Console.WriteLine("Run");
   }
}

W .NET 2.0 i 3.5 domyślnie maksymalna liczba wątków na procesor (rdzeń) to 250. W .NET 1.0 było to tylko 25. Powyższy kod najpierw tworzy wątki, które dopiero zostaną wykonane gdy ManualResetEvent wyśle sygnał. W tym problem, że wyczerpie to limit wolnych wątków i Signal nigdy nie zostanie wykonany.

Od wersji .NET 4.0 ta liczba ma charakter dynamiczny. W zależności od dostępnych zasobów może zostać zwiększona lub zmniejszona (mam na myśli wciąż wartość domyślną).

Problem może się wydawać niewarty uwagi ale w praktyce bardzo łatwo go popełnić. Jeśli korzystamy z zewnętrznych komponentów nie mamy pojęcia jak bardzo pula jest obciążona. Co jeśli mamy dostępne tylko 2 wątki? W takiej sytuacji, może okazać się, że bardzo prosty  problem spowoduje deadlock.

Powyższy przykład pokazuje również problem zaprezentowany w poprzednim wpisie – zbyt niska wartość minimalnej liczby wątków. Stworzenie 250 wątków zajmie ponad kilka minut!

Leave a Reply

Your email address will not be published.