In this article, we will explore multiple ways to call a method asynchronously using Delegates. You can call methods asynchronously in four different ways using the BeginInvoke() and EndInvoke() methods of the Delegate class. The four different ways are using the EndInvoke pattern, WaitHandle, Polling pattern and using a Callback Pattern. In this article, we will see the Delegate EndInvoke Pattern. In the subsequent articles, we will explore the Polling Pattern and Callback pattern.
A delegate is a special method that encapsulates a method and represents a method signature. From the main thread, when a method is invoked using a delegate, the Main thread has to wait until the method, that was invoked, has completed. This could prove expensive if your code is performing a slow operation like downloading a large file from your server.
Let us see an example of how a method is called synchronously using a Delegate:
As you can see, the Main method has to wait until the method SquareMethod completes. This is because SquareMethod was executed on the same thread, as that of the Main method.
However with a little effort, you can use delegates to call any method asynchronously (works when delegate has only one method in its invocation list). This is done using the BeginInvoke and EndInvoke methods of the Delegate class.
BeginInvoke, EndInvoke and IAsyncResult
The BeginInvoke method initiates the asynchronous call on a separate thread taken from the ThreadPool. It consists the same parameters required by the method (that you want to execute asynchronously) and consists two additional optional parameters called the callback parameter and the state parameter. The advantage of using BeginInvoke is that it returns immediately and does not wait for the asynchronous call to complete.
BeginInvoke returns a reference to an object implementing the IAsyncResult interface, which can be used to monitor the progress of the asynchronous call.
The EndInvoke method retrieves the results of the asynchronous call and releases the resource used by the thread. The parameters of EndInvoke include the out and ref parameters of the method that you want to execute asynchronously, plus a reference to the IAsyncResult returned by the BeginInvoke.
If the BeginInvoke() and EndInvoke() are not clear yet, worry not, as we will see an example shortly.
Call a Method Asynchronously using Delegate
Now that you know how the BeginInvoke and EndInvoke methods of the Delegate class work, let us see the different ways/patterns available to use BeginInvoke() and EndInvoke() to make asynchronous calls
EndInvoke Pattern - In this pattern, we use BeginInvoke from the Main thread to call the method, then do some processing on the main thread and then call EndInvoke(). This is useful when you want to have the calling thread to continue processing at the same time the asynchronous call is executing. Here the EndInvoke() does not return until the asynchronous call has completed. In other words, the main thread waits till the asynchronous operation is complete.
WaitHandle – Using this technique, the main method calls the method asynchronously and waits for a WaitHandle before it calls EndInvoke().
Polling Pattern - In this pattern, the calling thread polls the other thread (doing async operation) periodically using the IAsyncResult object and checks whether the thread has completed. If not, it continues processing and checks the thread later. The application does not call EndInvoke() until it knows that the operation is complete. This pattern can be useful when you want your UI to be responsive, till the async operation completes.
Callback Pattern - In this pattern, the initial thread initiates the async call but does not wait or check to see if the thread that was called, has completed. This pattern can be useful if you not want to process the results of the async call in the main thread.
Let us see an example of calling a method asynchronously using the EndInvoke Pattern
As you can see, BeginInvoke() is used to initiate the asynchronous call, which returns immediately after the call is made, so the Main method can continue processing while the async operation executes. The BeginInvoke() returns an IAsyncResult object to the calling thread.
IAsyncResult asyncRes = sd.BeginInvoke(10, null, null);
We then use the EndInvoke() function to retrieve the results of the asynchronous call. The EndInvoke() has a parameter that is a reference to the IAsyncResult, returned by the BeginInvoke.
int res = sd.EndInvoke(asyncRes);
EndInvoke() uses this parameter to find the thread it refers to, which can be used to monitor the progress of the asynchronous call.
That’s it. We just made an asynchronous call to a method using a Delegate!
In the next article, I will show you how to use the Polling pattern and Callback pattern to perform async calls on methods.
using System.Threading;
namespace DelegateAsyncMethods
{
delegate int SomeDelegate(int x);
/// <summary>
/// This code is written for the article
/// www.dotnetcurry.com/ShowArticle.aspx?ID=634
/// </summary>
class Program
{
static void Main(string[] args) {
SomeDelegate sd = SquareNumber; // Create delegate instance
Console.WriteLine("Before SquareNumber Method Invoke");
IAsyncResult asyncRes = sd.BeginInvoke(10, null, null);
Console.WriteLine("Back to Main Method");
int res = sd.EndInvoke(asyncRes);
Console.WriteLine(res);
Console.ReadLine();
}
static int SquareNumber(int a) {
Console.WriteLine("SquareNumber Invoked. Processing..");
Thread.Sleep(2000);
return a * a;
}
}
}
No comments:
Post a Comment