Multi-Process Synchronization with Named Events


Since I am in between real projects right now, I have had some time to work on some side projects.  As I was working on some threading in one of these projects, I noticed that there was a new class in the System.Threading namespace that had not been there in .NET 1.1 …  EventWaitHandle.  I was curious to see what this new class was.


Background Info:


Windows exposes several types of synchronization primitives (Mutexes, Events, etc.), but in .NET 1.1 we were limited in our use of them.  Sure, we had AutoResetEvent and ManualResetEvent, but the framework did not expose the “named” version of these.  In case you were wondering, the “named” version allows us to share an event between two (or more) processes.  The name makes it easy for multiple processes to say “give me ‘EventA’ “!!  So before 2.0, we had pretty good support for synchronization within a process, but not very good support across processes.  Yes, in .NET 1.1, we could get to this functionality using PInvoke, but it could get complicated quickly because you can attach security descriptors to an event.  And anyone who has tried to PInvoke to set some ACLs can tell you that security descriptor is a beast that you don’t want to be playing with in C#.  Fortunately this has been remedied in the 2.0 framework, and the Access Control Lists are now 1st class .NET objects.  I am going to go out on a limb and speculate that this is why named events were not included in the first version of the framework.


Back on Track:


“Great!” you say, but what the heck does this have to do with EventWaitHandle?  Well, EventWaitHandle is .NET 2.0’s implementation of events — both named and unnamed.  You can see that ManualResetEvent and AutoResetEvent now inherit from EventWaitHandle.


So, let’s get started and see what we can create with this.  In this example, WorkerA does some work, which notifies WorkerB who begins some work, which notifies WorkerC to finish up some work.  You can see that we are creating a simple pipeline.  (A real pipeline would keep working, but ours just exits after one iteration).


Let’s start by providing some tools for these events to get access to the events.


public static class NamedEvents


{


    public static EventWaitHandle OpenOrCreate(string name, bool initialState, EventResetMode mode)


    {


        EventWaitHandle ewh = null;


        try


        {


            ewh = EventWaitHandle.OpenExisting(name);


        }


        catch (WaitHandleCannotBeOpenedException)


        {


            //Handle does not exist, create it.


            ewh = new EventWaitHandle(initialState, mode, name);


        }


 


        return ewh;


    }


 


    public static EventWaitHandle OpenOrWait(string name)


    {


        EventWaitHandle ewh = null;


 


        while (null == ewh)


        {


            try


            {


                ewh = EventWaitHandle.OpenExisting(name);


            }


            catch (WaitHandleCannotBeOpenedException)


            {


                Thread.Sleep(50);


            }


        }


 


        return ewh;


    }


}


We’ve provided the tools to create an event if it doesn’t already exist or open it if it does, and to open an existing event or wait for it to be created.  Unfortunately there is not a good way to “test” if an event exists, so we just have to catch WaitHandleCannotBeOpenedException.  Here is our code for WorkerB, though the other workers are almost identical except that they set and wait on different events.


static void Main(string[] args)


{


    EventWaitHandle completedA = NamedEvents.OpenOrWait(“CompletedA”);


    EventWaitHandle completedB = NamedEvents.OpenOrCreate(“CompletedB”, false, EventResetMode.ManualReset);


    EventWaitHandle pipelineDone = NamedEvents.OpenOrWait(“PipelineDone”);


 


    Console.WriteLine(“{0} Initialized”, Process.GetCurrentProcess().ProcessName);


 


    completedA.WaitOne();


 


    for (int i = 0; i < 10; i++)


    {


        Thread.Sleep(250);


        Console.WriteLine(“{0} Working: {1:hh-mm-ss.ffff}”, Process.GetCurrentProcess().ProcessName, DateTime.UtcNow);


    }


 


    Console.WriteLine(“{0} Done”, Process.GetCurrentProcess().ProcessName);


 


    completedB.Set();


 


    //wait until the whole pipeline is done.


    pipelineDone.WaitOne();


 


    completedB.Close();


 


    Console.WriteLine(“{0} Exiting”, Process.GetCurrentProcess().ProcessName);


 


}




In the output, we can see that B waits for A to finish, and C waits for B to finish.



Now, you know how to use named events!!


BlogThreads.zip


Comments (11)

  1. Dmitry says:

    Hi.. What I don’t understand is in OpenOrCreate(…) method you’re trying to open an existing event handle in "try section".. What’s that for?

    If an event with the name already exists then new EventWaitHandle(..) will return an EventWaitHandler wrapper for that existing handler… It will create a new one, otherwise (take a look at the help on that)..

    Here, by handle, I mean the windows one as in Win API…

  2. Ahmed says:

    Can we pass data b/w the events using EventWaitHandle?

    or it is mainly for synchronization purpose?

  3. mattdotson says:

    Mainly for synchronization purposes.

  4. Roman says:

    Your "OpenOrCreate" code should just use the EventWaitHandle constructor version with last "out bool createdNew" parameter instead of your try/catch logic.

    Regarding your remark that there is no good way to test whether event exists – this would be meaningless by the very nature of multithreaded programs. Other process could create the handle right between your Test and Create calls, so your "Test" would tell you that handle does not exist – then you would attempt to create it – but this could fail because other process might opened handle between your Test and Create calls.

  5. RochLion says:

    This works fine across processes in the same session on Vista but does not work if you need to communicate between processes on different sessions.

    Example would be a process running as a Service under Session 0 to another process running under a Login in Session 1.

    The bottom of the following link talks about the system kernel objects being specific to the individual Session.

    http://msdn.microsoft.com/en-us/library/z4c9z2kt(VS.80).aspx

  6. Dzmitry Lahoda says:

    Dear Matt,

    Have you considered what can happen if during handling WaitHandleCannotBeOpenedException in OpenOrCreate other process will open mutex, so that creation will fail?

  7. dzmitry.lahoda says:

    I created multi process event passing with data inside, how it works is here http://www.youtube.com/watch. With link to code. It handles simultaneous starts and crashes of processes.