December 2017 : Online courses in ASP.NET MVC and Angular 4. Conducted by Bipin Joshi. Read more...
Registration for December 2017 batches of ASP.NET MVC / Core and Angular 4 online courses have already started. Conducted by Bipin Joshi. Book your seat today ! Click here for more details.

Invoking Asynchronous Tasks in A

Invoking Asynchronous Tasks in ASP.NET 2.0

Introduction

Invoking multiple tasks that are slow or lengthy always poses a challenge in front of developers. Normal approach is to develop a multithreaded component and delegate the tasks to it. In ASP.NET 1.x there was no direct support at web form level for executing such tasks in asynchronous manner. ASP.NET 2.0 on the other hand allows you to execute tasks asynchronously from within the web form itself. This article explores the two possible approaches with examples.

If you are not familiar with asynchronous operations then I suggest that you read Asynchronous Programming In .NET and Invoking Methods Asynchronously using Delegates for better understanding.

Possible approaches

There are two approaches that you can take for developing asynchronous pages:

  • Using RegisterAsyncTask method of Page class
  • Using AddOnPreRenderCompleteAsync method of Page class

Understanding the asynchronous task execution model

Before we delve further into the topic let's first understand how it works.

Under normal condition ASP.NET executes all the code in sequence. That means all the events such as Init, Load, PreRender and Unload are executed one after the other as follows:

  • PreInit
  • Init
  • InitComplete
  • PreLoad
  • Load
  • LoadComplete
  • PreRender
  • PreRenderComplete
  • Unload

When you enable asynchronous processing using any of the methods listed above, the sequence of operation changes as follows:

  • PreInit
  • Init
  • InitComplete
  • PreLoad
  • Load
  • LoadComplete
  • PreRender
  • Your asynchronous tasks are started
  • Your asynchronous tasks complete
  • PreRenderComplete
  • Unload

As you can see ASP.NET plugs in your asynchronous operations between PreRender and PreRenderComplete events. Note that ASP.NET blocks the rendering of the page till all the asynchronous tasks that you started are not complete.

When you run a web form having asynchronous operations, ASP.NET does the following:

  • ASP.NET picks up a thread from thread pool to execute your page
  • Till PreRender event that thread does the work and then it is returned to the thread pool
  • The aysnchronous operation is started
  • When the asynchronous operation completes, ASP.NET picks up another thread from the thread pool and executes remaining events on that thread

The Async attribute

Whatever approach you take for implementing the asynchronous tasks you need to set Async attribute of @Page directive to true.

<%@ Page Language="C#" Async="true" %>

NOTE: Strictly speaking it is not mandatory for RegisterAsyncTask that the page must have Async set to true. But using this attribute tells ASP.NET to use implementation of IHttpAsyncHandler internally.

BeginEventHandler and EndEventHandler delegates

When ASP.NET is about to start your asynchronous operation it expects a function to start your operation. The signature of this function must match the signature of BeginEventHandler delegate. The following code shows the signature of this delegate.

IAsyncResult BeginAsyncOperation
(object sender, EventArgs e, 
AsyncCallback cb, object state)

The AsyncCallBack parameter supplies the delegate to call when the asynchronous method call is complete. The state parameter can be used to pass some application specific state information. The return type of this function is IAsyncResult. Note that asynchronous pattern of .NET framework (BeginXXXX) always returns an object implementing this interface. This holds true for asynchronous web service calls, network programming tasks and file IO.

Just like BeginEventHandler there is another delegate called EndEventHandler. When the asynchronous task completes ASP.NET invokes a function that you supply. The function must match the ssignature of EndEventHandler delegate. The following code shows the signature:

void EndAsyncOperation(IAsyncResult ar)

This method receives an instance of IAsyncResult that you returned during BeginEventHandler.

These delegates are required in both of the asynchronous invocation approaches.

Using RegisterAsyncTask method

In order to illustrate the use of RegisterAsyncTask method we will develop two web services called WebService1.asmx and WebService2.asmx with a web method called HelloWorld. The HelloWorld web method looks as shown below:

[WebMethod]
public string HelloWorld(string name) 
{
System.Threading.Thread.Sleep(3000);
return "Hello " + name;
}

The web method takes a string parameter called name and returns a string by concatenating it to "Hello". Just to simulate a lengthy or slow operation we have introduced a delay by putting the thread to sleep for 3 seconds.

We will be calling this web method asynchronously from a web form.

In the code behind of the web form we need to create functions matching the signatures of BeginEventHandler and EndEventHandler delegates respectively. The following code shows these functions.

private IAsyncResult BeginAsyncOperation1
(object sender, EventArgs e,AsyncCallback cb, object state)
{
StreamWriter writer= 
File.AppendText(Server.MapPath("log1.txt"));
writer.WriteLine(DateTime.Now);
writer.Close();
WSProxy1.WebService1 obj1 = 
(WSProxy1.WebService1)state;
obj1 = new WSProxy1.WebService1();
return obj1.BeginHelloWorld("Tom",cb, state);
}
private void EndAsyncOperation1(IAsyncResult ar)
{
StreamWriter writer = 
File.AppendText(Server.MapPath("log1.txt"));
writer.WriteLine(DateTime.Now);
writer.Close();
WSProxy1.WebService1 obj1 = 
(WSProxy1.WebService1)ar.AsyncState;
Label1.Text = Label1.Text + "<br>" + 
obj1.EndHelloWorld(ar);
}
private void TimeoutAsyncOperation1(IAsyncResult ar)
{
//do nothing
}
private IAsyncResult BeginAsyncOperation2
(object sender, EventArgs e, 
AsyncCallback cb, object state)
{
StreamWriter writer= 
File.AppendText(Server.MapPath("log2.txt"));
writer.WriteLine(DateTime.Now);
writer.Close();
WSProxy2.WebService2 obj2 =
(WSProxy2.WebService2)state;
obj2 = new WSProxy2.WebService2();
return obj2.BeginHelloWorld("Jerry",cb, state);
}
private void EndAsyncOperation2(IAsyncResult ar)
{
StreamWriter writer = 
File.AppendText(Server.MapPath("log2.txt"));
writer.WriteLine(DateTime.Now);
writer.Close();
WSProxy2.WebService2 obj2 = 
(WSProxy2.WebService2)ar.AsyncState;
Label1.Text = Label1.Text + "<br>" + 
obj2.EndHelloWorld(ar);
}
private void TimeoutAsyncOperation2(IAsyncResult ar)
{
//do nothing
}

In the BeginXXXX functions we create an instance of web service proxies and start the asynchronous operation by calling BeginHelloWorld methods. The return value from BeginHelloWorld is returned from the BeginAsyncOperation1 and BeginAsyncOperation2 functions respectively. We also log the time stamp in a text file which will prove that the operations are indeed happening asynchronously.

In the EndXXXX functions we retrieve the instance of Proxy on which we called the BeginHelloWorld method and then call EndHelloWorld method on that proxy instance. As before we log time stamp in a log file.

The TimeoutXXXX function gets called in case the asynchronous task exceeds the timeout value. By default the timeout value is 45 seconds. You can configure it using the AsyncTimeout attribute of @Page directive. In our example we do not have anything specific in these functions.

<%@Page Language="C#" AsyncTimeout="30" %>

Now, let's see how to actually trigger these functions.

protected void Button1_Click(object sender, EventArgs e)
{
WSProxy1.WebService1 obj1=new WSProxy1.WebService1();
WSProxy2.WebService2 obj2=new WSProxy2.WebService2();
BeginEventHandler beginHandler1 = 
new BeginEventHandler(BeginAsyncOperation1);
EndEventHandler endHandler1 = 
new EndEventHandler(EndAsyncOperation1);
EndEventHandler timeoutHandler1 = 
new EndEventHandler(TimeoutAsyncOperation1);
object state1 = obj1;
PageAsyncTask task1 = 
new PageAsyncTask(beginHandler1, endHandler1, 
timeoutHandler1, state1,true);
BeginEventHandler beginHandler2 = 
new BeginEventHandler(BeginAsyncOperation2);
EndEventHandler endHandler2 = 
new EndEventHandler(EndAsyncOperation2);
EndEventHandler timeoutHandler2 =
 new EndEventHandler(TimeoutAsyncOperation2);
object state2 = obj2;
PageAsyncTask task2 = 
new PageAsyncTask(beginHandler2, endHandler2, 
timeoutHandler2, state2,true);
Page.RegisterAsyncTask(task1);
Page.RegisterAsyncTask(task2);
}

Here, we simply create instances of BeginEventHandler and EndEventHandler delegates and point them to our functions. We also create instances of web service proxies which are used in these functions.

The RegisterAsyncTask method of page class takes a parameter of type PageAsyncTask. While creating the instance of PageAsyncTask class you need to pass a delegates pointing to begin, end and timeout functions. Additionally you can pass application specific state (instances of web service proxies in our example). The last boolean parameter indicates whether the tasks are to be executed in parallel (simultaneously) or no. Since our tasks are independent of one another we set this parameter to true.

Calling RegisterAsyncTask method schedules the tasks on the thread pool. You can call this method several times to schedule multiple tasks.

Using AddOnPreRenderCompleteAsync method

In this approach the delegates and functions used are the same as we used above. However, you need to call AddOnPreRenderCompleteAsync method of the page class passing begin and end handlers. This method does not allow for timeout handling. You can call this method multiple times to invoke multiple asynchronous operations. Note that AddOnPreRenderCompleteAsync does not not allow you to call multiple tasks in parallel.

Here is a sample call to this method:

WSProxy1.WebService1 obj1 = 
new WSProxy1.WebService1();
BeginEventHandler beginHandler1 = 
new BeginEventHandler(BeginAsyncOperation1);
EndEventHandler endHandler1 = 
new EndEventHandler(EndAsyncOperation1);
object state1 = obj1;
Page.AddOnPreRenderCompleteAsync
(beginHandler1, endHandler1, state1);

Code Download

The complete source code of the above examples is available for download along with this download. Just click on the Download link on the top.

Summary

 ASP.NET 2.0 makes asynchronous programming easy by providing two approaches. In general if you want to execute multiple asynchronous tasks in parallel or otherwise then RegisterAsyncTask is neat and recommended where as if you want to execute just a single asynchronous task then AddOnPreRenderCompleteAsync method can be used.

 


Bipin Joshi is a software consultant, an author and a yoga mentor having 22+ years of experience in software development. He also conducts online courses in ASP.NET MVC / Core and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced the Yoga way of life he also teaches Meditation and Mindfulness to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 01 January 2006


Tags : ASP.NET Web Forms Multithreading Performance