views:

436

answers:

3

Let's say I have an exposed interface as such:

interface IMyService 
{
    MyResult MyOperation();
}

This operation is synchronous and returns a value.

My implemented interface has to do the following:

  • Call an asynchronous method
  • Wait for event #1
  • Wait for event #2

This is due to a 3rd party COM object I am working with.

This code looks similar to the following

public MyResult MyOperation()
{
    _myCOMObject.AsyncOperation();

    //Here I need to wait for both events to fire before returning
}

private void MyEvent1()
{
    //My Event 1 is fired in this handler
}

private void MyEvent2()
{
    //My Event 2 is fired in this handler
}

My two events can happen in either order, it is quite random.

What is the proper threading mechanism I can use to synchronize this? I was using ManualResetEvent before I had to start waiting for the second event, and have not seen an easy way to use it for both events. These 2 events set variables that allow me to create the return value for MyOperation().

Any ideas on a good implementation for this? I have no control over the way the 3rd party object is implemented.

+4  A: 

Two ManualResetEvents should do the trick for you. Just initialize them to false before you call the _myCOMObject.AsyncOperation(). Like this:

private ManualResetEvent event1;
private ManualResetEvent event2;

public MyResult MyOperation()
{
   event1 = new ManualResetEvent(false);
   event2 = new ManualResetEvent(false);

    _myCOMObject.AsyncOperation();

    WaitHandle.WaitAll(new WaitHandle[] { event1, event2 });
}

private void MyEvent1()
{
    event1.Set();
}

private void MyEvent2()
{
    event2.Set();
}

Edit

Thanks for the comments. I've changed the wait call to use WaitAll

Adam Robinson
This makes sense. My confusion lied on where to call Reset() on muliple ManualResetEvents, and I will also have to lock the entire call to MyOperation() so that 2 threads can't call the function at the same time.
Jonathan.Peppers
Whilst this is a perfectly fine solution, I would use WaitAll (shown below) instead of two WaitOne blocking calls, the WaitAll gives the system more context about the blocking, and potentially gives it chance to better schedule.
meandmycode
Thanks for the feedback. I've updated my answer.
Adam Robinson
Just be aware there's a limit to WaitAll of 64 handles I believe. If you need more than that, then just loop over the array and WaitOne for each.
Michael Hart
A: 

I am not sure I understood your question, but AutoResetEvent.WaitAll seems to solve your problem, if I got it right. It allows you to set more than one handler and it will only be released when all are set.

http://msdn.microsoft.com/en-us/library/z6w25xa6.aspx

Leahn Novash
+2  A: 

My implementation example is as follows:

namespace ConsoleApplication1
{

    class Program
    {
        private static WaitHandle[] waitHandles;
        private static event EventHandler Evt1;
        private static event EventHandler Evt2;

        static void Main(string[] args)
        {
            waitHandles = new WaitHandle[]{
                 new ManualResetEvent(false),
                 new ManualResetEvent(false)
            };

            Evt1 += new EventHandler(Program_Evt1);
            Evt2 += new EventHandler(Program_Evt2);

            OnEvt1();
            OnEvt2();

            WaitHandle.WaitAll(waitHandles);

            Console.WriteLine("Finished");
            Console.ReadLine();
        }

        static void Program_Evt2(object sender, EventArgs e)
        {
            Thread.Sleep(2000);
            ((ManualResetEvent)waitHandles[0]).Set();
        }

        static void Program_Evt1(object sender, EventArgs e)
        {
            ((ManualResetEvent)waitHandles[1]).Set();
        }

        static void OnEvt1()
        {
            if (Evt1 != null)
                Evt1(null, EventArgs.Empty);
        }

        static void OnEvt2()
        {
            if (Evt2 != null)
                Evt2(null, EventArgs.Empty);
        }


    }
}

I make it sleep for the purposes of this example and the WaitAll functionality

Cheers,

Andrew

P.S. another example would be using AsyncCallback, really quick and dirty example, but gives you more keys to open the door with :-) . Hope this helps!!

namespace ConsoleApplication1
{
    class Program
    {
        private static WaitHandle[] waitHandles;
        private static event EventHandler Evt1;
        private static event EventHandler Evt2;

        static void Main(string[] args)
        {
            waitHandles = new WaitHandle[]{
                 new ManualResetEvent(false),
                 new ManualResetEvent(false)
            };

            var callabck1 = new AsyncCallback(OnEvt1);
            var callabck2 = new AsyncCallback(OnEvt2);

            callabck1.Invoke(new ManualResetResult(null, (ManualResetEvent)waitHandles[0]));
            callabck2.Invoke(new ManualResetResult(null, (ManualResetEvent)waitHandles[1]));

            WaitHandle.WaitAll(waitHandles);

            Console.WriteLine("Finished");
            Console.ReadLine();

        }

        static void OnEvt1(IAsyncResult result)
        {
            Console.WriteLine("Setting1");
            var handle = result.AsyncWaitHandle;
            ((ManualResetEvent)handle).Set();
        }

        static void OnEvt2(IAsyncResult result)
        {
            Thread.Sleep(2000);
            Console.WriteLine("Setting2");
            var handle = result.AsyncWaitHandle;
            ((ManualResetEvent)handle).Set();
        }

    }

    public class ManualResetResult : IAsyncResult
    {
        private object _state;
        private ManualResetEvent _handle;

        public ManualResetResult(object state, ManualResetEvent handle)
        {
            _state = state;
            _handle = handle;
        }

        #region IAsyncResult Members

        public object AsyncState
        {
            get { return _state; }
        }

        public WaitHandle AsyncWaitHandle
        {
            get { return _handle; }
        }

        public bool CompletedSynchronously
        {
            get { throw new NotImplementedException(); }
        }

        public bool IsCompleted
        {
            get { throw new NotImplementedException(); }
        }

        #endregion
    }
}
REA_ANDREW