Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to Manage Multiple Asynchronous Calls by using Delegates

0.00/5 (No votes)
9 Aug 2010 1  
How to Manage Multiple Asynchronous Calls by using Delegates

Introduction

There are tons of ways on making asynchronous calls in .NET, and you should choose which one to use based on different scenarios. The scenario I want to cover here is how to deal with a situation where you need to make multiple asynchronous calls, and wait until all of them are completed in order to move on to the next step.

This is a typical situation in which you need to make multiple database calls, and wait for all the result sets to come back before continuing your process.

The Problem

In this example, you have a method SomeRandomNumber, which takes some time to execute, and will return an integer value. You will have to make 3 asynchronous calls to this method and wait until all of them are completed.

private static int SomeRandomNumber(int waitTime)
{
    Thread.Sleep(waitTime);
    return rnd.Next();
}

One of the most common solutions is to put all asynchronous calls to the ThreadPool, and you will need a loop to check if they are all completed, based on some global bool flag in your instance. First of all, the code is not clean, and this imperative style is hard to control in multiple threading situation. You can't be sure no one else will change your flag!

private int? threadOneResult;
private int? threadTwoResult;
private int? threadThreeResult;

public void AsyncCallsWithThreadPool()
{
    ThreadPool.QueueUserWorkItem(ThreadOne, 1000);
    ThreadPool.QueueUserWorkItem(ThreadTwo, 5000);
    ThreadPool.QueueUserWorkItem(ThreadThree, 10000);

    while (threadOneResult == null ||
        threadTwoResult == null ||
        threadThreeResult == null)
    {
        Thread.Sleep(100);
    }

    // continue
    Console.WriteLine(
        String.Format("Result: {0}\t{1}\t{2}",
        threadOneResult,
        threadTwoResult,
        threadThreeResult));
}

ThreadOne, ThreadTwo, and ThreadThree put back the result to the global int? variables. After all three threads are completed, the above method will get out of the while loop.

private void ThreadOne(object state)
{
    threadOneResult = SomeRandomNumber((int)state);
}

private void ThreadTwo(object state)
{
    threadTwoResult = SomeRandomNumber((int)state);
}

private void ThreadThree(object state)
{
    threadThreeResult = SomeRandomNumber((int)state);
}

The Solution

The methods BeginInvoke and EndInvoke will do all the work for you. BeginInvoke will process your asynchronous call in a new thread. EndInvoke will return the result from that thread. If the thread is not completed when you are calling EndInvoke, it will wait. You don't have to implement your wait loop in the main thread. The status of each asynchronous operation is saved in the IAsyncResult object, so one delegate can handle multiple invokes.

This approach can eliminate all the unnecessary global variables, so your asynchronous logic does not need to rely on the state in this instance. This looks clean. The most important thing is that this keeps your threading structure simple.

public void AsyncCallsInDelegate()
{
    GetNumber deleg = new GetNumber(SomeRandomNumber);

    IAsyncResult rst1 = deleg.BeginInvoke(1000, null, null);
    IAsyncResult rst2 = deleg.BeginInvoke(5000, null, null);
    IAsyncResult rst3 = deleg.BeginInvoke(10000, null, null);

    Console.WriteLine(
        String.Format("Result: {0}\t{1}\t{2}",
        deleg.EndInvoke(rst1),
        deleg.EndInvoke(rst2),
        deleg.EndInvoke(rst3)));
}

Life is getting better in .NET 3.5. You can use the generic delegate Func<int> to save the work of declaring a new delegate:

public void AsyncCallsInGenericDelege()
{
    Func<int> deleg = new Func<int>(SomeRandomNumber);

    IAsyncResult rst1 = deleg.BeginInvoke(1000, null, null);
    IAsyncResult rst2 = deleg.BeginInvoke(5000, null, null);
    IAsyncResult rst3 = deleg.BeginInvoke(10000, null, null);

    Console.WriteLine(
        String.Format("Result: {0}\t{1}\t{2}",
        deleg.EndInvoke(rst1),
        deleg.EndInvoke(rst2),
        deleg.EndInvoke(rst3)));
}

History

  • 8/8/2010 - First release on CodeProject

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here