AutoResetEvent\ManualResetEvent–synchronizacja między procesami

AutoResetEvent\ManualResetEvent może być używany do synchronizacji międzyprocesowej tak samo jak np. mutex. Posiada podobny zestaw metod do tworzenia obiektu z nazwą oraz późniejszego jego otwierania.

Aby móc go użyć do synchronizacji międzyprocesowej należy oczywiście nadać obiektowi nazwę – tak samo jak to jest z Mutex. W tym problem, że konstruktory ManualResetEvent czy AutoResetEvent nie przyjmują takich parametrów. Zaglądając jednak do dokumentacji dowiemy się, że:

public sealed class ManualResetEvent : EventWaitHandle

public sealed class AutoResetEvent : EventWaitHandle

Obie klasy dziedziczą po EventWaitHandle, którego konstruktor wygląda następująco:

public EventWaitHandle(bool initialState,EventResetMode mode);

public EventWaitHandle(bool initialState,EventResetMode mode,string name);

public EventWaitHandle(bool initialState,EventResetMode mode,string name,out bool createdNew);

public EventWaitHandle(bool initialState,EventResetMode mode,string name,out bool createdNew,EventWaitHandleSecurity eventSecurity);

Tak naprawdę ManualResetEvent oraz AutoResetEvent to wrappery, ułatwiające pracę ze zdarzeniami. Możemy bezpośrednio użyć EventWaitHandle i przekazać  nazwę. Stwórzmy więc proces A, który użyje takiego konstruktora:

internal class Program
{
   public static void Main()
   {
       var manualResetEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "TestEvent");
       Console.WriteLine("Utworzono obiekt EventWaitHandle");
       manualResetEvent.WaitOne();
   }
}

Następnie w procesie B, chcemy odczytać ten obiekt i np. zamknąć aplikację, gdy on istnieje (używaliśmy mutex’ów do tego samego celu):

class Program
{
   static void Main(string[] args)
   {
       try
       {
           EventWaitHandle eventHandle = EventWaitHandle.OpenExisting("TestEvent");
       }
       catch(WaitHandleCannotBeOpenedException)
       {
           Console.WriteLine("Nie udało się otworzyć obiektu.");
           return;
       }
       Console.WriteLine("Obiekt znaleziony.");                
   }
}

Gdy nie ma obiektu o podanej nazwie wyrzucany jest wyjątek WaitHandleCannotBeOpenedException. W przeciwnym wypadku możemy korzystać z niego, tak jakby został on utworzony w tym samym procesie (Set, Wait itp.).

Co jeśli wywołamy kontruktory tworzące ten sam obiekt w dwóch różnych procesach? Tzn.:

// Proces A
internal class Program
{
   public static void Main()
   {
       var manualResetEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "TestEvent");
       manualResetEvent.WaitOne();
       Console.WriteLine("Sygnal odebrany.");
   }
}
// Proces B
class Program
{
   static void Main(string[] args)
   {
       var manualResetEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "TestEvent");
       manualResetEvent.Set();
       Console.WriteLine("Sygnal wyslany.");
   }
}

Proces B, automatycznie otworzy obiekt zainicjalizowany w procesie A.  W wielu przypadkach to jest logiczne i pożądane rozwiązanie ale co w przypadku gdy Proces A utworzył ManualResetEvent, z kolei proces B AutoResetEvent?

// Proces A
internal class Program
{
   public static void Main()
   {
       var manualResetEvent = new EventWaitHandle(false, EventResetMode.ManualReset, "TestEvent");
       manualResetEvent.WaitOne();
       Console.WriteLine("Sygnal odebrany.");
   }
}
// Proces B
class Program
{
   static void Main(string[] args)
   {
       var manualResetEvent = new EventWaitHandle(false, EventResetMode.AutoReset, "TestEvent");
       manualResetEvent.Set();
       Console.WriteLine("Sygnal wyslany.");
   }
}

Z opisanych przyczyn powinniśmy unikać takiej konstrukcji ponieważ nie wiemy, czy zdarzenie zostało utworzone czy otworzone – stąd nie mamy pojęcia o jego typie, stanie itp. Istnieje inny konstruktor, który poinformuje nas, jaka operacja została właściwie wykonana:

public EventWaitHandle(
    bool initialState,
    EventResetMode mode,
    string name,
    out bool createdNew
)

Parametr createNew określa, czy zdarzenie jest nowe czy zostało tylko otworzone. Pamiętajmy, że gdy zdarzenie jest otwierane przez proces B, to nieważne jaki stan (lub typ) przekażemy – zawsze będzie używany ten pierwotny z procesu A.

One thought on “AutoResetEvent\ManualResetEvent–synchronizacja między procesami”

  1. Zarzadzaniem procesami, czyli miedzy innymi kiedy ktoremu przyznac kwant czasu procesora zajmuje sie system operacyjny. Dzieki sprawnemu przelaczeniu pomiedzy wykonywanymi procesami na maszynach jednoprocesorowych, gdzie moze byc wykonywany tylko jeden proces jednoczesnie, uzytkownik nie odczuwa dyskomfortu gdy pracuje z wieloma aplikacjami jednoczesnie.

Leave a Reply

Your email address will not be published.