Metoda ThreadPool.RegisterWaitForSingleObject

Wielowątkowość jest bardzo skomplikowanym tematem. W celu napisania prostego kodu wystarczy znać naprawdę niewiele konstrukcji. Sytuacja znaczącą komplikuje się gdy mamy wysokie wymagania odnoście pamięci czy CPU.

RegisterWaitForSingleObject jest metodą dość mało popularną a przydatną gdy chcemy oszczędzić trochę pamięci.Wyobraźmy sobie, że mamy kod, który chcemy wykonać wyłącznie w momencie zasygnalizowania przez WaitHandle  (AutoResetEvent, Semafor itp.). Jeśli operacja wewnątrz zajmuje trochę czasu wtedy wątek będzie blokowany przez długi czas. Przykład:

internal static class Sample
{
   private static AutoResetEvent _autoResetEvent = new AutoResetEvent(false);

   public static void Main()
   {
       Task.Factory.StartNew(Run1);

       Console.ReadLine();

       _autoResetEvent.Set();

       Console.ReadLine();
   }
   private static void Run1()
   {
       _autoResetEvent.WaitOne();
       Console.WriteLine("Time-consuming operation.");
   }
}

AutoResetEvent w Run1 będzie blokował i trzymał zasoby wątku w pamięci aż WaitHandle wyśle sygnał.  RegisterWaitForSingleObject pozwala uniknąć tego poprzez przekazanie semafora oraz callback’a, który powinien został wywołany w momencie otrzymania sygnału. Definicja:

public static RegisteredWaitHandle RegisterWaitForSingleObject(
    WaitHandle waitObject,
    WaitOrTimerCallback callBack,
    Object state,
    int millisecondsTimeOutInterval,
    bool executeOnlyOnce
)

Przykład:

internal static class Sample
{
   private static AutoResetEvent _autoResetEvent=new AutoResetEvent(false);

   public static void Main()
   {
       RegisteredWaitHandle result1 = ThreadPool.RegisterWaitForSingleObject(_autoResetEvent, Run1, null, Timeout.Infinite, true);            
       
       Console.ReadLine();

       _autoResetEvent.Set();

       Console.ReadLine();

       result1.Unregister(_autoResetEvent);
   }        
   private static void Run1(object state, bool timedOut)
   {
       Console.WriteLine("Time-consuming operation.");         
   }   
}

RegisterWaitForSingleObject wywoła Run1 na osobnym wątku jeśli WaitHandle da sygnał. Można przekazać stan, timeout oraz flagę określającą czy Callback powinien zostać wywołany tylko raz. Pamiętajmy, że np. AutoResetEvent może wysłać sygnał wielokrotnie – za każdym wywołaniem Set. Dobrym zwyczajem jest odrejestrowane delegaty po wszystkim – ułatwia to pracę GC.

TimeOut.Infinite oznacza, że timeout jest nieskończony i delegata zawsze będzie zarejestrowana. Gdy podamy mniejsze wartości to w momencie przekroczenia czasu, Run1 byłby wykonany z przekazanym parametrem timeout równym false.

Podsumowując, jeśli korzystamy z mechanizmów synchronizacyjnych i wiemy, że otrzymanie sygnału zajmie trochę czasu (sekcja krytyczna itp.) wtedy lepiej wykorzystać RegisterWaitForSingleObject, która nie blokuje żadnych wątków – wątek zostanie otworzony w momencie, gdy podane kryteria zostaną spełnione.

Leave a Reply

Your email address will not be published.