Click here to Skip to main content
15,886,788 members
Articles / Programming Languages / C#

Extending the IAsyncResult Pattern for Multiple Parameter Function Calls

Rate me:
Please Sign up or sign in to vote.
4.95/5 (10 votes)
22 Mar 2010CPOL5 min read 35.4K   19   4
How to make your methods callable asynchronously by creating begin/end pair stems, in a similar way that the WSDL.exe tool generates the contract files for a Web Service.

Introduction

One of the interesting features in .NET framework programming is the ability to easily use asynchronous programming and multi threading. .NET offers a wide variety of methods for asynchronous programming and for working with threads, but this was made very much easier with .NET 2.0. Building multithreaded applications in .NET 1.0 and .NET 1.1 has been made very convenient with classes like Thread and ThreadStart delegates. The ThreadPool class is useful in managing threads in multithreaded applications.

In a quick glance, we can see that the addition of the BackgroundWorker class has added to the Windows application tool set.

We can do an asynchronous callback for ASP.NET pages by adding the attribute:

ASP.NET
<%@ Page Async="true" ... %>

This has made it very easy for users (even beginners) to use these facilities.

I will not talk about how to use multithreading in .NET, this is out of the scope of this article and the internet is full of such articles.

I will be talking about how to make your methods callable asynchronously by creating begin/end pair stems, in a similar way that the WSDL.exe tool generates the contract files for a Web Service. You will need this when you make a service or something like that and you want others to use it in an asynchronous way, to make it easy for them to implement it and enhance the performance without the need for them to create more threads and manage them etc.

Background

I got really interested in the subject when I was developing a Smart Client application from scratch. In the beginning, I made a Web Service and coded all its functionality. Then, I built up a Windows client application which consumed the functionality of the service, and in order to enhance performance, I consumed the service in its Begin/End pair of methods, asynchronously.

Obviously, I did not care how these functions had to act or how they were done, all I needed to do was to call them properly, pass the proper parameters, and obtain the proper results. The problem popped up when I needed to implement the same functions - I am using from the Web Service - again, in order to work in offline mode. In this case, I had to create my own set of Begin/End pair or methods so that they could be used by the Windows client application when running in offline mode.

The IAsyncResult interface

First, let's have a swift look at the famous interface..

C#
public interface IAsyncResult{
  object AsyncState { get; }
  WaitHandle AsyncWaitHandle { get; }
  bool CompletedSynchronously { get; } bool IsCompleted { get; } 
}

As we can see, it has only four properties that need to be implemented..

  • AsyncState: the simplest, to hold the state parameter..
  • WaitHandle is an important property which gets the WaitHandle object..
  • CompletedSynchronously indicates whether the method was executed synchronously or not. But in our case, it always returns false; since it uses the thread pool, it will always be executed asynchronously.
  • IsCompleted indicates whether the execution was completed or not yet.

By surfing the internet you can find some good articles on this. However, the one I admired the most was the one done by luisabreu (http://csharpfeeds.com/post/11390/Multithreading_implementing_the_IAsyncResult_interface) where he wrote a neat article about implementing the interface, on which I made some additions and tweaks to customise it for my code.

Implementing the IAsyncResult interface and calling the functions asynchronously

Here is the code by luisabreu with a small addition from my side:

C#
internal class AsynchronousResult<T,TResult>: IAsyncResult 
{ 
   private volatile Boolean _isCompleted; 
   private ManualResetEvent _evt;
   private readonly AsyncCallback _cbMethod;
   private readonly Object _state;
   private TResult _result;
   private Exception _exception;
   private readonly T _Parameteres;

   public AsynchronousResult(Func<T, TResult> workToBeDone, 
          T Parameteres, AsyncCallback cbMethod, Object state)
   {

       _cbMethod = cbMethod;
       _state = state;
       _Parameteres = Parameteres;
       QueueWorkOnThreadPool(workToBeDone);
   }

   private void QueueWorkOnThreadPool(Func<T,TResult > workToBeDone) {
       ThreadPool.QueueUserWorkItem(state => {
            try {
                _result = workToBeDone( _Parameteres);
            } catch (Exception ex) {
                _exception = ex;
            } finally {
                UpdateStatusToComplete(); //1 and 2
                NotifyCallbackWhenAvailable(); //3 callback invocation
            }
       });
    }

    public TResult FetchResultsFromAsyncOperation() {
        if (!_isCompleted) {
            AsyncWaitHandle.WaitOne();
            AsyncWaitHandle.Close();
        }
        if (_exception != null) {
            throw _exception;
        }
        return _result;
    }

    private void NotifyCallbackWhenAvailable() {
        if (_cbMethod != null) {
            _cbMethod(this);
        }
    }

    public object AsyncState {
        get { return _state; }
    }

    public WaitHandle AsyncWaitHandle {
        get { return GetEvtHandle(); }
    }

    public bool CompletedSynchronously {
        get { return false; }
    }

    public bool IsCompleted {
        get { return _isCompleted; }
    }

    private readonly Object _locker = new Object();

    private ManualResetEvent GetEvtHandle() {
        lock (_locker) {
            if (_evt == null) {
                _evt = new ManualResetEvent(false);
            }         
            if (_isCompleted) {
                _evt.Set();
            }
        }
        return _evt;
    }

    private void UpdateStatusToComplete() {
        _isCompleted = true; //1. set _iscompleted to true
        lock (_locker) {
            if (_evt != null) {
                _evt.Set(); //2. set the event, when it exists
            }
        }
    }
}

private readonly T _Parameteres was my main addition, allowing the pattern to work for functions which take any number of parameters and returns a value..

However, in case you want to call procedures which return no values and take no parameters, there is another similar, yet simpler, implementation, but I went for the most difficult and complex one.

FetchResultsFromAsyncOperation is a helper function that makes sure the operation is completed and then checks for exceptions raised by that operation, and then it returns the value or re-raises the exception again..

ManualResetEvent allows threads to communicate with each other by signaling. Typically, this communication concerns a task which a thread must complete before other threads can proceed.

When a thread begins an activity that must complete before other threads proceed, it calls Reset to put ManualResetEvent in the non-signaled state. This thread can be thought of as controlling the ManualResetEvent. Threads that call WaitOne on the ManualResetEvent will block, awaiting the signal. When the controlling thread completes the activity, it calls Set to signal that the waiting threads can proceed. All waiting threads are then released.

Once it has been signaled, ManualResetEvent remains signaled until it is manually reset. That is, calls to WaitOne return immediately.

You can control the initial state of a ManualResetEvent by passing a boolean value to the constructor: true if the initial state is signaled, and false otherwise.

ManualResetEvent can also be used with the static WaitAll and WaitAny methods.

How it works

The main function

C#
public T_ACTIVITIESRow[] GetActivities(string sqlWhere, string sqlOrder, int User_ID)
{
    if (string.IsNullOrEmpty(sqlWhere)) sqlWhere = "1=1";
    if (string.IsNullOrEmpty(sqlOrder)) sqlOrder = "TIMESTAMP";
    IQueryable<T_ACTIVITIESRow> Filtered = 
       _Ds._ACTIVITIESlst.AsQueryable().Where(sqlWhere).OrderBy(sqlOrder);
    return Filtered.ToArray();
}

The overloaded function

C#
private T_ACTIVITIESRow[] GetActivities(object[] arg)
{
    if (string.IsNullOrEmpty(arg[0] as string )) arg[0] = "1=1";
    if (string.IsNullOrEmpty(arg[1] as string )) arg[1] = "TIMESTAMP";
    return GetActivities(arg[0] as string, arg[1] as string, (int)arg[2]);
}

Implementation of the Begin function

C#
IAsyncResult BeginGetActivities(string sqlWhere, string sqlOrder, 
             int User_ID, AsyncCallback callback, object asyncState)
{
    AsynchronousResult<object[], T_ACTIVITIESRow[]> ar = 
       new AsynchronousResult<object[], T_ACTIVITIESRow[]>(GetActivities, 
       new object[]{(object)sqlWhere, (object)sqlOrder, (object)User_ID }, 
                    callback, asyncState);
    return ar;
}

The whole Implementation of the End Function

C#
T_ACTIVITIESRow[] IllafWSSoap.EndGetActivities(IAsyncResult result)
{
    object ResultedValue = 
      ((AsynchronousResult<object[], T_ACTIVITIESRow[]>)result).AsyncState;
    return (((AsynchronousResult<object[], 
              T_ACTIVITIESRow[]>)result).FetchResultsFromAsyncOperation());
}

I have a method called GetActivities that takes three parameters: the first two are of type string, while the third is an int. Since the parameters are heterogeneous, I will have to put them in the most generic array, which is object[].

Here is all I have to do:

  1. Overload the function GetActivities() in order to accept all the parameters as one of type object[].
  2. In BeginGetActivities, all you need to do is to create a new instance of AsynchronousResult with the proper parameters.
    C#
    AsynchronousResult<object[], T_ ACTIVITIESRow []> ar = 
       new AsynchronousResult<object[], T_ ACTIVITIESRow Row[]>(
       GetActivities, new[] { (object)sqlWhere, (object)sqlOrder, 
       (object)User_ID }, callback, asyncState);
    return ar;
  3. In EndGetActivities, we type the following:
    C#
    return (((AsynchronousResult<object[], 
              T_ ACTIVITIESRow []>)result).FetchResultsFromAsyncOperation());

    and that is it.

Once we implement the IAsyncResult interface, it will be so easy for us to implement the needed functions..

I hope this has helped you.. though I know it might need more explanation. However, I am open to all your questions..

Conclusion

Implementing the IAsyncResult interface and using it will enable you to provide an easy and trustful way to call your methods asynchronously and build a robust application with good performance.

Links

This article was based on this one:

Other useful links:

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
United States United States
I graduated as an electronic engineer at 2000, and I have been working in software development ever since.
Interested mainly in .NET technologies.

Comments and Discussions

 
GeneralI alwyas want to know more about this Pin
Yves31-Mar-10 13:57
Yves31-Mar-10 13:57 
GeneralRe: I alwyas want to know more about this Pin
Assil31-Mar-10 20:46
professionalAssil31-Mar-10 20:46 
QuestionCan you change the title to a more specific one? Pin
Arthur Milfait25-Mar-10 7:56
Arthur Milfait25-Mar-10 7:56 
AnswerRe: Can you change the title to a more specific one? Pin
Assil29-Mar-10 22:47
professionalAssil29-Mar-10 22:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.