Introduction
It is not a new topic, yet it can't be old. This topic is a very important to know, and the deeper you know about it, the stronger grasp you will have on the .NET platform.
Nowadays, and especially after .NET 4.0, I see asynchronous programming has become a trend that everyone is following. If you have built a platform or a framework or even a class library, and you still want to support it, it is highly recommended to create asynchronous counterparts for each and every time-consuming method you have in your library.
Background
This article is a late continuation for my previous articles talking about similar topics:
Well again, whether you are writing ASP.NET applications, Windows applications or .NET Assemblies, and you want to benefit from asynchronous capabilities, you have many means and options to choose from.
However, this article is not about the tactics you would use to make asynchronous calls, it is about the strategies you design your application on, hence, it is about Patterns and Models.
As MSDN documentation states, there are three patterns to build your application, or refactor your library to use asynchronous capabilities:
Asynchronous Programming Patterns
Title
| Description
|
Asynchronous Programming Model (APM)
| Describes the legacy model that uses the <a href="http://msdn.microsoft.com/en-us/library/system.iasyncresult.aspx">IAsyncResult</a> interface to provide asynchronous behavior.
This model is no longer recommended for new development.
|
Event-based Asynchronous Pattern (EAP)
| Describes the event-based legacy model for providing asynchronous behavior. This model is no longer recommended for new development.
|
Task-based Asynchronous Pattern (TAP)
| Describes the new asynchronous pattern based on the <a href="http://msdn.microsoft.com/en-us/library/system.threading.tasks.aspx">System.Threading.Tasks</a> namespace. This model is the recommended approach to asynchronous programming in the .NET Framework 4 and later versions.
|
Using the Code
The code is the fun part, and in the sample that I have for you, I have implemented the three patterns:
Asynchronous Programming Model (APM)
This model is not recommended anymore and it is outdated, yet it is very simple and very practical.
It is to be used in legacy systems, in all systems prior to .NET 4.5, and it might also be useful in WCF when you want to create a contract that supports asynchronous calls. Let’s assume that we have a time consuming method that takes two integer parameters and returns an integer.
public int GetPrimeCount(int min, int count)
{
PrintCurrentThreadId("GetPrimeCount");
return ParallelEnumerable.Range(min, count).Count(n =>
Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i =>
n % i > 0));
}
Asynchronous programming model depends on the following:
- A delegate that represents the address of the function to be called.
- Divide the calling functionality into 2 portions or begin/end pair
- An optional callback function.
These are the basics of this model, yet you can do many things or shape it in different ways.
public IAsyncResult BeginGetPrimeCount(int min,
int count, AsyncCallback callback, object userState)
{
getPrimeCountCaller = this.GetPrimeCount;
PrintCurrentThreadId("BeginGetPrimeCount");
return getPrimeCountCaller.BeginInvoke(min, count, callback, userState);
}
public int EndGetPrimeCount(IAsyncResult result)
{
PrintCurrentThreadId("EndGetPrimeCount");
return getPrimeCountCaller.EndInvoke(result);
}
The delegate would look like this one:
private delegate int GetPrimeCountHandler(int min, int count);
private GetPrimeCountHandler getPrimeCountCaller;
My implementation for this pattern did not require me to expose the delegate as public, nor did it require me to use a call back function.It also did not require making use of the IAsyncResult and its AsyncWaitHandle.
However, as you can see in the EndGetPrimeCount(), I used the delegate getPrimeCountCaller which I could have done without it using this line:
((GetPrimeCountHandler)((AsyncResult)result).AsyncDelegate).EndInvoke(result);
The whole purpose of this is to call the method in another thread.
We can’t get the result unless we call EndInvoke(), which can be called in the callback function if we had to create one.
Event-based Asynchronous Pattern (EAP)
This pattern is also not recommended any more, it can be implemented in different ways, however it depends on the following:
- Your method should be executed asynchronously.
- It should raise an event once the execution has come to an end.
In order to implement this you need to define an event, that event can be designed the way you want, but it should just deliver the value of the result.
For this example, I chose it to look like this:
public delegate void GetPrimeCountEventHandler(object sender, GetPrimeEventArg e);
public event GetPrimeCountEventHandler GetPrimeCount_Completed;
Where GetPrimeEventArg is defined as:
public int GetPrimeCount(int min, int count)
{
Program.PrintCurrentThreadId("GetPrimeCount");
return ParallelEnumerable.Range(min, count).Count(n =>
Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i =>
n % i > 0));
}
You could choose it to be even simpler.Now the challenge is to make the call async, and that can be done in one of many ways including using the delegates shown in the paragraph above. Additionally, You can use Thread, ThreadStart, Threadpool, BackgroundWorker, or any other technique of your choice.
For this example, I choose ThreadPool for it is much faster than the delegate technique, I had a difficulty though because the ThreadPool accepts only a delegate that returns void and takes one parameter of an object which is not the case for me.
However I was able to find a workaround shows in here:
public void GetPrimeCountAsync(int min, int count)
{
int result = -1;
ManualResetEvent evt = new ManualResetEvent(false);
WaitCallback wait = new WaitCallback((x) =>
{
try
{
result = GetPrimeCount(min, count);
}
catch (Exception ex)
{
}
finally
{
evt.Set();
}
});
ThreadPool.QueueUserWorkItem(wait);
evt.WaitOne();
evt.Close();
On_GetPrimeCount_Completed(new GetPrimeEventArg()
{ Minimum = min, Count = count, Result = result });
}
That snippet was only an example; it can have more enhancement and polish. ThreadPool will guarantee to execute in a separate thread, while the ManualResetEvent will block the thread till you release it manually by calling the Set() method.
Task-based Asynchronous Pattern (TAP)
Now this is the new pattern that is recommended to use, it works only in .NET 4.5, and for using it, please go to this article: Asynchronous Programming in C# 5.0 using async and await.
In short you can use await in any method that is decorated with async keyword.
The return type can be only void, Task, or Task<T>.
That is enough to make it awaitable, and that means you can call it using await keyword in any other async method.
The method name ends with Async for tradition.
public async Task <int> GetPrimeCountAsync(int min, int count)
{
return await Task.Run<int>(() =>
{
Program.PrintCurrentThreadId("GetPrimeCount");
return ParallelEnumerable.Range(min, count).Count(n =>
Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i =>
n % i > 0));
});
}
This is the recommended pattern and it is much easier to implement, it can also be implemented in different ways however, the main thing in implementation is using the keywords async, and await while the framework will take care of multithreading.
The performance is outstanding.
Points of Interest
Asynchronous programming helps you to write and build vigorous applications and systems, it enables you to make use of the multithreading capabilities and exploit the power of machines..NET is making it easier in every release.
Sample of results: