Search code examples
c#multithreadingasynchronous.net-2.0

Wait for multiple methods with callbacks to finish


I have multiple asynchronous methods that I need to call and wait for them all to finish. The asynchronous methods all have callback parameters that execute when the asynchronous processing has finished. So I'm thinking that I need to put some signalling device in each callback and then wait for all the signals to come before continuing.

After doing some searching I'm thinking I could use a an array of AutoResetEvents and then call WaitHandle.WaitAll to wait for them. Something like:

WaitHandle[] waitHandles = new WaitHandle[] 
{
    new AutoResetEvent(false),
    new AutoResetEvent(false)
};

DoWorkA((callback) =>
{
    waitHandles[0].Set();
});

DoWorkB((callback) =>
{
    waitHandles[1].Set();
});

// wait for DoWorkA and DoWorkB to finish
WaitHandles.WaitAll(waitHandles);

// continue with the processing

I'd like to know if this is a good way to do it, or if there is a better/simpler method. I need an solution for .Net 2.0, but I'd also be interested to know what other solutions would be available with a later version of .Net.

EDIT: After trying this code I found it doesn't work. The UI thread stops at WaitHandles.WaitAll(waitHandles) and the callbacks never run...

Inside the DoWork methods there is code being run asnychronously on a background thread, and then from that background thread I attempt to run the callback through BeginInvoke called on a control from the UI thread. However, the callback never gets executed on the UI thread. Any ideas what I'm doing wrong?

EDIT 2: I realized why it doesn't work - the thread calling WaitHandles.WaitAll(waitHandles) is the same thread that the callbacks are attempting to run on (the UI thread). So of course if the thread is waiting for the signal, the callbacks will never run on that same thread and so Set() will never be called. The WaitAll(waitHandles) and Set() should work, as long as they are not inteded to be run on the same thread. I think I need to fix it so that the callbacks run on a background thread while the UI thread waits. Please let me know if my thinking here is incorrect.


Solution

  • Yes using events is reasonable way to wait for multiple callback completion and for 2.0 it is probably the best you can do if you can't use external libraries.

    You may rewrite code with one event and counting callback completion, but it may be more complicated (also allow unlimited number of callbacks at the same time). Or similar approach may be implemented via Semaphore.

    If you can use 4.5+ than rewriting methods with async/await may be better option.

      // use "async Task(...)" as equivalent of "void DoWorkA(...)".
      async Task<int> DoWorkAAsync(int arg1)
      {
         var serviceResult = await service.CallAsync(arg1);
         return ParseServiceResult(serviceResult);
      }
    
      async Task<int> DoWorkBAsync() 
      { 
         ... await SomeAsync(1,2,3);...
         return someResult;
      }
    
      var tasks = new[] {DoWorkAAsync(42), DoWorkBAsync() }; 
      await Task.WhenAll(tasks);
    
      var resultOfA = tasks[0].Result;