jeudi 14 octobre 2010

Sync C# - exemple de EventWaitHandle

Introduction
Un EventWaitHandle est un mécanisme permettant à un processus de signaler (setter) un évènement à l'intention d'un autre processus (listener).
Dans le monde Unix, ce procédé de communication est couramment dénommé signal, ce qui à mon sens est plus parlant. Le grand avantage de ce procédé de communication est que si l'on utilise des EventWaitHandle nommés (identifié par une string), il est alors possible de signaler l'évènement en cross-process.
Les implémentations les plus connues de EventWaitHandle sont les ManualResetEvent et AutoResetEvent.
L'AutoResetEvent doit être vu comme un portillon d'accès de métro.
Quand on insère le ticket, il est possible à une seule personne de passer le portillon... il se referme juste après.
Le processus qui met le ticket est celui qui fait l'opération "set" (le setter).
Le processus qui attend de passer le portillon est celui qui fait l'opération d'attente "WaitOne" (le listener).

La différence entre le ManuelResetEvent et l'AutoResetEvent, c'est que lorsque l'on fait un set sur un ManuelResetEvent, le portillon reste toute porte ouvertes (jusqu'à l'appel du Reset).

Exemple
L'exemple ci-dessous est composé de deux codes sources et donc deux programmes distincts.
  • Sync_NamedEventWaitHandle_Listener.cs attend simplement que l'évènement soit signalé (il utilise pour cela un AutoResetEvent nommé).
  • Sync_NamedEventWaitHandle_Setter.cs attend que l'on frappe au clavier pour signaler l'évènement.
A noter que lorsque l'on utilise un EventWaitHandle nommé, il est nécessaire de passer par un constructeur spécifique de la classe WaitEventHandle.
Démarrer les deux programmes et presser ENTER sur le "setter" permet de constater que l'évènement est reçu par le "listener"

Note: Tous les exemples sont compilés à l'aide de Snippet Compiler.

Le listener
Source: Sync_NamedEventWaitHandle_Listener.cs

using System;
using System.Collections.Generic;
using System.Threading;

public class MyClass
{
    private const string EVENT_NAME = "BQSoft.Sample.Synch.NamedEventWaitHandle.SomethingCHanged";
    private static EventWaitHandle evSomethingChanged = new EventWaitHandle( false, EventResetMode.AutoReset, EVENT_NAME );
    
    public static void RunSnippet()
    {        
        while( true ){
            WL( "Waiting for the event to occurs" );
            evSomethingChanged.WaitOne();
            WL( "HEY! Someting get changed" );
        }            
    }
    
    #region Helper methods
    
    public static void Main()
    {
        try
        {
            RunSnippet();
        }
        catch (Exception e)
        {
            string error = string.Format("---\nThe following error occurred while executing the snippet:\n{0}\n---", e.ToString());
            Console.WriteLine(error);
        }
        finally
        {
            Console.Write("Press any key to continue...");
            Console.ReadKey();
        }
    }

    private static void WL(object text, params object[] args)
    {
        Console.WriteLine(text.ToString(), args);    
    }
    
    private static void RL()
    {
        Console.ReadLine();    
    }
    
    private static void Break() 
    {
        System.Diagnostics.Debugger.Break();
    }

    #endregion
}

Le Setter
Source: Sync_NamedEventWaitHandle_Setter.cs

using System;
using System.Collections.Generic;
using System.Threading;

public class MyClass
{
    private const string EVENT_NAME = "BQSoft.Sample.Synch.NamedEventWaitHandle.SomethingCHanged";
    private static EventWaitHandle evSomethingChanged = new EventWaitHandle( false, EventResetMode.AutoReset, EVENT_NAME );
    
    public static void RunSnippet()
    {        
        while( true ){
            WL( "Press ENTER to signal the Event to the other process." );
            RL();
            evSomethingChanged.Set();            
        }            
    }
    
    #region Helper methods
    
    public static void Main()
    {
        try
        {
            RunSnippet();
        }
        catch (Exception e)
        {
            string error = string.Format("---\nThe following error occurred while executing the snippet:\n{0}\n---", e.ToString());
            Console.WriteLine(error);
        }
        finally
        {
            Console.Write("Press any key to continue...");
            Console.ReadKey();
        }
    }

    private static void WL(object text, params object[] args)
    {
        Console.WriteLine(text.ToString(), args);    
    }
    
    private static void RL()
    {
        Console.ReadLine();    
    }
    
    private static void Break() 
    {
        System.Diagnostics.Debugger.Break();
    }

    #endregion
}

Aucun commentaire: