Asynchronous Pages
I was trying to acquire a bank statement for my cash balance from my bank account, so I went to the bank customer service department and I found that they have a set of people (about 5 people) that help customers to find their services. They just ask the customer what service he/she needs then they go to a computer that I’m not allowed to touch to see if the requested service could be done or not. At a time only 5 customers can be served simultaneously and after a short time the bank will have a long queue of people waiting just for inquires about services, so I waited all day long just to acquire a banks statement. The reason that why I’m telling this story is that it describes the way ASP.NET processing incoming requests.
When ASP.NET receives a request for a page, it grabs a thread from a thread pool and assigns that request to the thread. A normal, or synchronous, page holds onto the thread for the duration of the request, preventing the thread from being used to process other requests. If a synchronous request becomes I/O bound—for example, if it calls out to a remote Web service or queries a remote database and waits for the call to come back—then the thread assigned to the request is stuck doing nothing until the call returns. That impedes scalability because the thread pool has a finite number of threads available which is 20 threads by default and a queued request backlog of 1000 request. If all request-processing threads are blocked waiting for I/O operations to complete, additional requests get queued up waiting for threads to be free. At best, throughput decreases because requests wait longer to be processed. At worst, the queue fills up and ASP.NET fails subsequent requests with 503 "Server Unavailable" errors.
Asynchronous operations are typically used to perform tasks that might take a long time to complete, such as opening large files, connecting to remote computers, or querying a database. An asynchronous operation executes in a thread separate from the main application thread. When an application calls methods to perform an operation asynchronously, the application can continue executing while the asynchronous method performs its task.
Asynchronous pages offer a neat solution to the problems caused by I/O-bound requests. Page processing begins on a thread-pool thread, but that thread is returned to the thread pool once an asynchronous I/O operation begins in response to a signal from ASP.NET. When the operation completes, ASP.NET grabs another thread from the thread pool and finishes processing the request. Scalability increases because thread-pool threads are used more efficiently. Threads that would otherwise be stuck waiting for I/O to complete can now be used to service other requests. The direct beneficiaries are requests that don't perform lengthy I/O operations and can therefore get in and out of the pipeline quickly. Long waits to get into the pipeline have a disproportionately negative impact on the performance of such requests.
ASP.NET 3.5 supports multiple patterns for asynchronous programming :
- Asynchronous operations that use IAsyncResult objects [Begin\End pattern].
- Asynchronous operations that use events.
- Asynchronous page task pattern.
Now let us start our investigations for each pattern.
- Asynchronous operations that use IAsyncResult objects [Begin\End pattern].
The .NET Framework enables you to call any method asynchronously. To do this you define a delegate with the same signature as the method you want to call; the common language runtime automatically defines BeginInvoke and EndInvoke methods for this delegate, with the appropriate signatures as follows :
1. I will create a new web site and a new web service with the default method HelloWorld:
[WebMethod]
public string HelloWorld(int SleepValue)
{
System.Threading.Thread.Sleep(SleepValue);
return "Hello World";
}
2. In the web site, define a delegate with the same signature as the method we want to call asynchronously:
private delegate string AsynchHelloWorld(int SleepValue);
3. Define the callback method that will handle asynchronous call competition:
private void AsyncCallBack(IAsyncResult result)
{
AsynchHelloWorld caller = ((AsyncResult)result).AsyncDelegate as AsynchHelloWorld;
if (result.IsCompleted)
{
this.txtResult.Text = caller.EndInvoke(result);
Trace.Warn(string.Format("Current Thread Id is {0}", Thread.CurrentThread.ManagedThreadId));
}
}
4. We now can call our web service asynchronously as follows:
localhost.Service svc = new AsynchronousPages.localhost.Service();
AsynchHelloWorld async = new AsynchHelloWorld(svc.HelloWorld);
AsyncCallback callback = new AsyncCallback(this.AsyncCallBack);
IAsyncResult result = async.BeginInvoke(SLEEP_VALUE, callback, null);
Trace.Warn(string.Format("Current Thread Id is {0}", Thread.CurrentThread.ManagedThreadId));
result.AsyncWaitHandle.WaitOne();
when we enable page trace, we will notice that our web service call is being executed on two threads as can be observed below :
This is the old style of calling synchronous methods asynchronously by using delegates, consider that you have 100 methods to be called which will need 100 delegates and another 100 callbacks which made the Begin/End pattern so tough.
- Asynchronous operations that use events:
The Event-based Asynchronous Pattern makes available the advantages of multithreaded applications while hiding many of the complex issues inherent in multithreaded design.A class that supports the Event-based Asynchronous Pattern will have one or more methods named MethodNameAsync. These methods may mirror synchronous versions, which perform the same operation on the current thread. The class may also have a MethodNameCompleted event and it may have a MethodNameAsyncCancel (or simply CancelAsync) method.
The Event-based Asynchronous Pattern may take several forms, depending on the complexity of the operations supported by a particular class. The simplest classes may have a single MethodNameAsync method and a corresponding MethodNameCompleted event. More complex classes may have several MethodNameAsync methods, each with a corresponding MethodNameCompleted event, as well as synchronous versions of these methods. Classes can optionally support cancellation, progress reporting, and incremental results for each asynchronous method.
In order to implement Event-based Asynchronous Pattern we will do the following :
1. Add the Async Tag to page header directive :
<%@ Page Async="true" %>
2. ASP.NET generates asynchronous method signature for each web method in our web servce, so we will subscribe in our method completion handler as follows :
localhost.Service svc = new AsynchronousPages.localhost.Service();
svc.HelloWorldCompleted += new AsynchronousPages.localhost.HelloWorldCompletedEventHandler(svc_HelloWorldCompleted);
svc.HelloWorldAsync(SLEEP_VALUE);
Trace.Warn(string.Format("Current Thread Id is {0}", Thread.CurrentThread.ManagedThreadId));
3. Now all we need to do is to implement the logic for event completion handler:
private void svc_HelloWorldCompleted(object sender, AsynchronousPages.localhost.HelloWorldCompletedEventArgs e)
{
if (!e.Cancelled)
{
this.txtResult.Text = e.Result;
Trace.Warn(string.Format("Current Thread Id is {0}", Thread.CurrentThread.ManagedThreadId));
}
}
if we run the page using this pattern we will have a trace like the one shown earlier. But what if our web service method take 5 Mins to run then the page will timeout after 90 seconds which is the default timeout for ASP.NET pages, so the problem with this pattern that it does not provide timeout facility.
- Asynchronous page task pattern:
ASP.NET provides another way to call methods asynchronously; each web page contains a method called “RegisterAsyncTask” that allow registration for asynchronous method execution. The benefit of using this mechanism over the previously described ones is :
- Timeout facility for long running tasks.
- Allow multiple parallel tasks to be executed asynchronously.
- Allow object state to be passed to begin call.
- Provide context to End/Timeout handlers [not like normal Begin/End Pattern].
let us see how to implment this pattern :
1. The first thing to do is to add timeout value for asynchronous tasks:
<%@ Page Async="true" AsyncTimeout="1" %>
2. The we will need to implment Begin/End and Timeout handlers as follows :
private IAsyncResult BeginTaskCall(object sender, EventArgs e,AsyncCallback cb, object state)
{
m_Request = WebRequest.Create("http://intellecting.net/blog/");
Trace.Warn(string.Format("Current Thread Id is {0}", Thread.CurrentThread.ManagedThreadId));
// uncomment to test timeout handling.
//Thread.Sleep(SLEEP_VALUE);
return m_Request.BeginGetResponse(cb, state);
}
private void EndTaskCall(IAsyncResult result)
{
Trace.Warn(string.Format("Current Thread Id is {0}", Thread.CurrentThread.ManagedThreadId));
WebResponse response = m_Request.EndGetResponse(result);
this.txtResult.Text = response.ContentLength.ToString();
}
private void TimeoutTaskCall(IAsyncResult result)
{
Trace.Warn(string.Format("Current Thread Id is {0}", Thread.CurrentThread.ManagedThreadId));
this.txtResult.Text = "Task timeout.";
}
3. finally we will register our task for exceution as follows :
PageAsyncTask Task = new PageAsyncTask(BeginTaskCall, EndTaskCall, TimeoutTaskCall, null, true);
Page.RegisterAsyncTask(Task);
Use Asynchronous ASP.NET pages whenever you need to execute a long running task:
- Database Query.
- Web Service Call.
- Generating an Image.
- Disk loading.
- Network streaming.
I included the source code that demonstrates all ASP.NET asynchronous page patterns here Asynchronous.zip (44.47 kb)
Currently rated 4.3 by 7 people
- Currently 4.285714/5 Stars.
- 1
- 2
- 3
- 4
- 5