Invoking Methods Asynchronously using Delegates
Introduction
Asynchronous operation improves the program responsiveness and availability.
In my past article titled
Asynchronous
Programming In .NET , I explained how to design your components with built-in
support for asynchronous operations. But what if some one has already developed
the component and that too without asynchronous programming support? In such
cases delegates can come to the rescue. In fact that is the topic of this
article.
Delegates and asynchronous operations
Delegates provide a built-in way to call a method asynchronously. Whenever
you create an instance of the delegate, it automatically provides you with two
methods.
- BeginInvoke(parameter list, AsyncCallback callback, object state)
- EndInvoke(IAsyncResult result)
Using the BeginInvoke method you call the method under consideration. When
the method finishes, .NET calls the supplied callback function inside which you
are supposed to call the EndInvoke method. Behind the scene these methods
use threads from the .NET ThreadPool.
BeginInvoke method
The BeginInvoke method accepts all the parameters as per the method
signature. For example, if your delegate signature is:
public delegate int AddNumbersDelegate(int x,int y);
then BeginInvoke will accept x and y as first two parameters.
Next comes the AsyncCallback instance. This is another delegate that points
to the callback function that will be called when the method completes. The
callback function mentioned above must have following signature:
public void MyCallback(IAsyncResult result)
Finally, you can pass some state information to the method via state
parameter. This is useful in case you want to pass some information other than
method parameters.
EndInvoke method
The EndInvoke method takes only one parameter that is of type IAsyncResult.
At runtime this parameter provides information about the state information being
passed, delegate that is invoking the method etc. Also, the EndInvoke method
gives you the return value of the method being invoked.
Example
Let's see how it works with an example. The example consists of three classes
- Class1, Class2 and Class3. Class1 is the class that contains Main() and is
used to actually invoke the method in asynchronous manner. Class2 is the class
that contains a method (Add()) that we want to call asynchronously. Class3
contains a method (MyCallback()) that will be supplied as a callback to the
BeginInvoke() method.
using System;
using System.Runtime.Remoting.Messaging;
namespace AsyncViaDelegates
{
public delegate int AddNumbersDelegate(int x,int y);
class Class1
{
[STAThread]
static void Main(string[] args)
{
Class2 c2=new Class2();
Class3 c3=new Class3();
AsyncCallback callback=new AsyncCallback(c3.MyCallback);
int state=100;
AddNumbersDelegate d=new AddNumbersDelegate(c2.Add);
d.BeginInvoke( 10,20,callback,state);
System.Threading.Thread.Sleep(3000);
}
}
class Class2
{
public int Add(int a,int b)
{
return a+b;
}
}
class Class3
{
public void MyCallback(IAsyncResult result)
{
AsyncResult ar=(AsyncResult)result;
AddNumbersDelegate d=
(AddNumbersDelegate)ar.AsyncDelegate;
int state=(int)ar.AsyncState;
int i=d.EndInvoke(result);
Console.WriteLine(i);
Console.WriteLine(state);
Console.ReadLine();
}
}
}
- We begin by importing two namespaces. The second one is important as it
contains AsyncResult class used later in the code.
- We declared a delegate called AddNumbersDelegate that accepts two
integers and returns an integer
- The class Class2 is the class that actually contains the function that
we want to call asynchronously. In our example this function is Add()
- Note that since we want to call this function via the delegate our
delegate signature matches with the signature of Add.
- The function simply accepts two integer parameters and returns sum of
those parameters
- We also have Class3 that contains the definition of the callback
function (MyCallback)
- In the Main() method we created the instances of Class2 and Class3
- Then we created an instance of AsyncCallback delegate by passing
reference to the MyCallback method. This instance will passed to the
BeginInvoke() method later
- We then declared an integer variable called state. This variable do not
take part in any processing. We are using it just to illustrate how to pass
the state information to the delegates
- Next, we created the instance of AddNumbersDelegate delegate called d
and pass the reference of Add() method to it.
- Normally i.e. in synchronous mode you would have invoked this delegate
as d(10,20) but here we want to invoke the Add() method asynchronously and
hence we used the BeginInvoke() method of the delegate.
- As explained earlier, the BeginInvoke method accepts all the parameters
of the actual method (Add) followed by a callback function and state
information.
- We then halt the program execution for 3 seconds so that the callback
function (MyCallback) will be called. In real life situation you can perform
some other processing instead.
- The callback function receives an instance of a class that implements
IAsyncResult interface. .NET framework contains one such class called
AsyncResult. In fact instance of this class is what you receive in the
callback function.
- The AsyncResult class contains a property called AsyncDelegate that
gives the reference to the delegate on which you called BeginInvoke()
method. It also provides the state information passed while calling
BeginInvoke() through the AsyncState property.
- Using the delegate reference mentioned above we called EndInvoke()
method on the delegate. The return value of Add() is obtained via this call
to EndInvoke() method.
- Finally, we simply print the results on the console.
Summary
.NET provides native support for asynchronous operations. This article
explained how to invoke methods asynchronously using delegates. This method is
useful when the component developer has not provided asynchronous support for
the component but you still want to call the methods asynchronously. The
disadvantage of this method, however, is that you need to define your own
delegate.