3

I have run into this problem across multiple programming languages and I was just wondering what the best way to handle it is.

I have three method calls that fire off asynchronously. Each one has a callback. I want to do something only when all three callbacks have completed.

What is the best way to code this? I usually end up with all these public bool flags and as you add more calls the code gets more convoluted.

Xander
  • 9,069
  • 14
  • 70
  • 129

5 Answers5

3

Coming from C#, I would probably use WaitHandle.WaitAll. You can create an array of ManualResetEvent objects (one for each task to be completed), and pass that array to WaitAll. The threaded tasks will get one ManualResetEvent object each, and call the Set method when they are ready. WaitAll will block the calling thread until all tasks are done. I'll give a C# code example:

private void SpawnWorkers()
{
    ManualResetEvent[] resetEvents = new[] {
            new ManualResetEvent(false), 
            new ManualResetEvent(false)
        };

    // spawn the workers from a separate thread, so that
    // the WaitAll call does not block the main thread
    ThreadPool.QueueUserWorkItem((state) =>
    {
        ThreadPool.QueueUserWorkItem(Worker1, resetEvents[0]);
        ThreadPool.QueueUserWorkItem(Worker2, resetEvents[1]);
        WaitHandle.WaitAll(resetEvents);
        this.BeginInvoke(new Action(AllTasksAreDone));

    });
}
private void AllTasksAreDone()
{
    // OK, all are done, act accordingly
}

private void Worker1(object state)
{
    // do work, and then signal the waiting thread
    ((ManualResetEvent) state).Set();
}

private void Worker2(object state)
{
    // do work, and then signal the waiting thread
    ((ManualResetEvent)state).Set();
}

Note that the AllTasksAreDone method will execute on the thread pool thread that was used to spawn the workers, and not on the main thread... I assume that many other languages have similar constructs.

Fredrik Mörk
  • 155,851
  • 29
  • 291
  • 343
1

If you really only want to wait for all to finish:

  1. Create volatile counter
  2. Synchronize access to counter
  3. Increase counter on start
  4. Decrease on callback fired
  5. Wait for counter to reach 0
jitter
  • 53,475
  • 11
  • 111
  • 124
0

Use a semaphore.

patros
  • 7,719
  • 3
  • 28
  • 37
0

Futures are very easy to use. Futures look like normal functions, except that they execute asynch.

The classes:



public struct FutureResult<T>
{
    public T Value;
    public Exception Error;
}
public class Future<T>
{
    public delegate R FutureDelegate<R>();
    public Future(FutureDelegate<T> del)
    {
        _del = del;
        _result = del.BeginInvoke(null, null);
    }
    private FutureDelegate<T> _del;
    private IAsyncResult _result;
    private T _persistedValue;
    private bool _hasValue = false;
    private T Value
    {
        get
        {
            if (!_hasValue)
            {
                if (!_result.IsCompleted)
                    _result.AsyncWaitHandle.WaitOne();
                _persistedValue = _del.EndInvoke(_result);
                _hasValue = true;
            }
            return _persistedValue;
        }
    }
    public static implicit operator T(Future<T> f)
    {
        return f.Value;
    }
}

Here I use futures to simulate a deadlock:


        void SimulateDeadlock()
        {
            Future> deadlockFuture1 = new Future>(() =>
            {
                try
                {
                    new SystemData(ConfigurationManager.ConnectionStrings["DbConnectionString"].ConnectionString)
                        .SimulateDeadlock1(new DateTime(2000, 1, 1, 0, 0, 2));
                    return new FutureResult { Value = true };
                }
                catch (Exception ex)
                {
                    return new FutureResult { Value = false, Error = ex };
                }
            });
            Future> deadlockFuture2 = new Future>(() =>
            {
                try
                {
                    new SystemData(ConfigurationManager.ConnectionStrings["DbConnectionString"].ConnectionString)
                        .SimulateDeadlock2(new DateTime(2000, 1, 1, 0, 0, 2));
                    return new FutureResult { Value = true };
                }
                catch (Exception ex)
                {
                    return new FutureResult { Value = false, Error = ex };
                }
            });
            FutureResult result1 = deadlockFuture1;
            FutureResult result2 = deadlockFuture2;
            if (result1.Error != null)
            {
                if (result1.Error is SqlException && ((SqlException)result1.Error).Number == 1205)
                    Console.WriteLine("Deadlock!");
                else
                    Console.WriteLine(result1.Error.ToString());
            }
            else if (result2.Error != null)
            {
                if (result2.Error is SqlException && ((SqlException)result2.Error).Number == 1205)
                    Console.WriteLine("Deadlock!");
                else
                    Console.WriteLine(result2.Error.ToString());
            }
        }

Hans Malherbe
  • 2,988
  • 24
  • 19
0

For those using JavaScript, consider using the pattern discussed at this Stackoverflow question: javascript: execute a bunch of asynchronous method with one callback

Community
  • 1
  • 1
Scott Mitchell
  • 8,659
  • 3
  • 55
  • 71