Asynchronous ASP.NET 2.0 Programming
Asynchronous ASP.NET 2.0 Programming
One of the coolest features in ASP.NET 2.0 is asynchronous pages. This feature increase scalability by using thread-pool threads more efficiently.
With asynchronous pages, a thread which assigned from the thread-pool to process page request, is returned to the thread pool instead of waiting for an I/O operation to complete. This means more threads available to process incoming requests, and when I/O operation is completed, another thread from the pool is guarantee to send back the response.
There are 3 asynchronous programming model in ASP.NET 2.0:
- Asynchronous Pages
- Asynchronous HTTP Handlers
- Asynchronous HTTP Modules
In this post I will cover the asynchronous pages programming model.
Asynchronous Pages in ASP.NET 2.0
Let's start by including an Async="true" attribute in the @Page directive. This tells ASP.NET to implement IHttpAsyncHandler in the page.
<%@ Page="" Async="true" Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
AddOnPreRenderCompleteAsync
In Page_Load (or any early Page's life time) we can call to the AddOnPreRenderCompleteAsync method, to register begin and end methods, as shown in the following code:
using System;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Net;
using System.IO;
public partial class _Default : System.Web.UI.Page
{
private WebRequest _request;
protected void Page_Load(object sender, EventArgs e)
{
AddOnPreRenderCompleteAsync(
new BeginEventHandler(BeginAsyncOperation),
new EndEventHandler(EndAsyncOperation)
);
}
IAsyncResult BeginAsyncOperation(object sender, EventArgs e,
AsyncCallback asyncCallback, object state)
{
_request = WebRequest.Create("http://blogs.microsoft.co.il/blogs/egady");
return _request.BeginGetResponse(asyncCallback, state);
}
void EndAsyncOperation(IAsyncResult asyncResult)
{
using (WebResponse response = _request.EndGetResponse(asyncResult))
{
using (StreamReader reader =
new StreamReader(response.GetResponseStream()))
{
string str = reader.ReadToEnd();
Response.Write(str);
}
}
}
}
The BeginAsyncOperation can launch an asynchronous operation such database call, or web service call, and return immediately. When this operation is completed, another thread continue from the EndAsyncOperation method to complete the request and send the response back to the client!
The result of the code is showing in the following image:
RegisterAsyncTask
What if you want to perform several asynchronous I/O operations in an asynchronous page which not involved remote web service/database calls? We can call AddOnPreRenderCompleteAsync multiple times, and it will cause the tasks to execute sequentially - next begin method not called until previous end method returns. For concurrent execution it will requires you to compose IAsyncResult.
The RegisterAsyncTask can helps us with this, by calling it multiple times (one per task). The tasks can be executed sequentially or concurrently, and prevent us from implementing IAsyncResult!
The following code cause to the same result like the above sample with AddOnPreRenderCompleteAsync:
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
using System.Net;
using System.IO;
public partial class Default2 : System.Web.UI.Page
{
private WebRequest _request;
protected void Page_Load(object sender, EventArgs e)
{
PageAsyncTask task = new PageAsyncTask(
new BeginEventHandler(BeginAsyncOperation),
new EndEventHandler(EndAsyncOperation),
new EndEventHandler(TimeoutAsyncOperation), null
);
RegisterAsyncTask(task);
}
IAsyncResult BeginAsyncOperation(object sender, EventArgs e,
AsyncCallback asyncCallback, object state)
{
_request = WebRequest.Create("http://blogs.microsoft.co.il/blogs/egady");
return _request.BeginGetResponse(asyncCallback, state);
}
void EndAsyncOperation(IAsyncResult asyncResult)
{
using (WebResponse response = _request.EndGetResponse(asyncResult))
{
using (StreamReader reader =
new StreamReader(response.GetResponseStream()))
{
string str = reader.ReadToEnd();
Response.Write(str);
}
}
}
void TimeoutAsyncOperation(IAsyncResult asyncResult)
{
}
}
You can decide if the task will run in parallel or sequentially by the fifth parameter of the PageAsyncTask constructor:
ExecuteRegisteredAsyncTasks - Execute registered async tasks immediately, and synchronously - Please don't use this method!!
Context Flow
Another advantage of RegisterAsyncTask is that it flows request context from begin threads to end threads, and that includes:
-
HttpContext
-
Impersonation
-
Culture
While in AddOnPreRenderCompleteAsync HttpContext.Current will be null in end threads, and end threads also revert to process identity.
Conclusion
Asynchronous programming can make your application more scalable, by using the ASP.NET thread-pool more efficiently. When you take the 5-10 pages in your site, that are used mostly and have the heaviest remote I/O operations, and convert them into asynchronous pages, you can more than double your traffic into your site!
References: