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:
<%@ 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..
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:
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(); NotifyCallbackWhenAvailable(); }
});
}
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; lock (_locker) {
if (_evt != null) {
_evt.Set(); }
}
}
}
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
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
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
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
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:
- Overload the function
GetActivities()
in order to accept all the parameters as one of type object[]
.
- In
BeginGetActivities
, all you need to do is to create a new instance of AsynchronousResult
with the proper parameters.
AsynchronousResult<object[], T_ ACTIVITIESRow []> ar =
new AsynchronousResult<object[], T_ ACTIVITIESRow Row[]>(
GetActivities, new[] { (object)sqlWhere, (object)sqlOrder,
(object)User_ID }, callback, asyncState);
return ar;
- In
EndGetActivities
, we type the following:
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: