Async Pages in ASP.NET 2.0 - some results

Onion Blog

Syndication

I finally had a chance to get back to looking at the new asynchronous page feature coming in 2.0, and contrary to my initial impression, I'm now quite pleased with the implementation. My last post on the subject discussed what actually happens when you mark a page with the Async="true" attribute. What I have now tried, is actually building some asyncronous pages that perform lengthy web service invocations, and the results look pretty good. Here's the rundown:
First, I deployed a web service to a remote server that just slept for 3 seconds and returned a string.
Next, I wrote an .aspx page that invoked the webservice synchronously.
Then, I wrote an asynchronous .aspx page that invoked the webservice asyncronously as follows:
SlowWSAsync.aspx:
<%@ Page Language="C#" Async="true" CompileWith="SlowWSAsync.aspx.cs"
               ClassName="PS.Samples.SlowWSAsync_aspx" %>
 
SlowWsAsync.aspx.cs:
 
using System;
 
namespace PS.Samples
{
  public partial class SlowWSAsync_aspx
  {
    Slow slowWebservice = new Slow();
    void Page_Load(object sender, EventArgs e)
    {
      BeginEventHandler bh = new BeginEventHandler(this.BeginGetAsyncData);
      EndEventHandler eh = new EndEventHandler(this.EndGetAsyncData);
      AddOnPreRenderCompleteAsync(bh, eh);
    }
 
    IAsyncResult BeginGetAsyncData(Object src, EventArgs args, AsyncCallback cb, Object state)
   {
      // Note - this is serviced on the same thread as Page_Load
      // but a different thread is used to service EndGetAsyncData
      //
      return slowWebservice.BeginHelloWorld(cb, state);
    }
 
    void EndGetAsyncData(IAsyncResult ar)
    {
      string ret = slowWebservice.EndHelloWorld(ar);
      Response.Write(AppDomain.GetCurrentThreadId());
    }
  }
}
 
Notice that it is now possible to write a page that is serviced asynchronous with respect to the request thread by setting Async='true' and registering two event handlers with 'AddOnPreRenderCompleteAsync'. The internal ProcessRequest implemenation of the page class will execute up to the PreRender event and then invoke the 'BeginEventHandler' if you have registered one. When the IAsyncResult returned from the BeginEventHandler (in my case named BeginGetAsyncData) is signaled because the asynchronous operation is complete, it completes the page lifecycle by rendering and then unloading the page.
 
The beauty of this model, is you can still write your page as you normally would, and just spin off and perform an asynchronous operation before the page is rendered and the response is returned. You of course need to be very careful of how you do this asynchronous work, but if you're making a web service invocation, the work is done for you, as the WebRequest class underneath the web service proxy will use Async I/O to perform the request, exactly what you want for this situation. Invoking a web service synchronously blocks the request thread performing no useful work while it awaits the result of the web service invocation. Shifting to this asynchronous model means that the request thread is freed up to service other requests while you await the return of the web service invocation. If you aren't performing a web request, keep in mind that launching an asynchronous delegate here will not help in relieving pressure on the threadpool since it draws from the same threadpool as that the request threads are drawn from.
 
To test my asynchronous page, I set up a test harness that invoked 200 concurrent requests to the web server from a separate client machine. 100 of the requests were made to a trivial page called fast.aspx, and 100 requests were made to either the synchronous or asynchronous page that invoked the web service. The idea was to see if I could clog the threadpool to impede request times for fast.aspx with slow web service requests, and then fix the problem by invoking the web service asynchronously with the async page mechanism. Here are the results:
 

Sync slow page and fast page 100 requests each

        total requests issued: 100
        distinct threads used: 33
        average request time : 11.511252368
        total requests issued: 100
        distinct threads used: 33
        average request time : 18.876843568

Async slow page and fast page 100 requests each

        total requests issued: 100
        distinct threads used: 2
        average request time : 17.448289408
        total requests issued: 100
        distinct threads used: 2
        average request time : 0.0636915840000001
My test harness records both the time each request takes, as well as the server-side thread id used to service the request thread (to determine how many distinct threads were used to service requests). As you can see, in the first test, the fast.aspx requests averaged over 11 seconds even though the page was utterly trivial, and the CPU was idle - the threadpool was clogged with long running pages performing synchronous web service requests. The second test used the asynchronous page, and as you can see, relieved pressure on the threadpool nicely so that the average fast.aspx page was serviced in 0.06 seconds. It also reduced the overall number of threads used to 2 because the load on the threadpool was reduced significantly.
 
So, the simplicity of the new asynchronous page model combined with asynchronous web service invocation in ASP.NET 2.0 creates a pretty compelling model for invocation - especially in this increasingly service-oriented world of ours :)

Posted Oct 19 2004, 02:44 PM by fritz-onion
Filed under:

Comments

TheChaseMan's Frenetic SoapBox wrote Fritz Onion Reports on Asynch Pages in ASP.NET 2.0
on 10-19-2004 1:07 PM
aaron weiker wrote More Async info in ASP.NET 2.0
on 10-19-2004 6:44 PM
Sergio Pereira wrote re: Async Pages in ASP.NET 2.0 - some results
on 10-20-2004 5:17 AM
Fritz, so what happens after the BeginEventHandler returns the IAsyncResult object? Is that thread returned to the pool and the page object goes to "HttpHandler Purgatory" waiting to be rescued by the EndEventHandler ? Will the ASP.NET 2.0 runtime have a queue of sleeping page objects waiting to be awaken?
Fritz Onion wrote re: Async Pages in ASP.NET 2.0 - some results
on 10-20-2004 5:41 AM
After the BeginEventHandler returns the IAsyncResult object, ASP.NET registers a delegate to be called back when the work is complete, and releases the request thread back to the pool. That's a good way of putting it actually - the page going to 'HttpHandler Purgatory' :) because that's exactly what happens. Once the async request finishes and calls back into the delegate registered by ASP.NET it 'awakens' the page and completes its request cycle. It's wrong to think of the sleeping pages as being in a queue - they're just handlers sitting in memory to be serviced when the async request finishes. There is no implicit ordering imposed.
Mike Taulty's Weblog wrote Fritz Onion on ASP.NET 2.0 and Asynchronous Web Pages
on 10-21-2004 7:13 AM
Some Assembly Required wrote Async Pages and HttpContext.Current
on 10-27-2004 12:08 PM
ERP_CodeWarrior wrote re: Async Pages in ASP.NET 2.0 - some results
on 11-22-2004 1:15 PM
What is the rationale behind the architecture put forth above?
David Taylor wrote re: Async Pages in ASP.NET 2.0 - some results
on 01-11-2005 10:10 PM
Yes Fritz, this is great news. Plus with delegate inference, this:

BeginEventHandler bh = new BeginEventHandler(this.BeginGetAsyncData);
EndEventHandler eh = new EndEventHandler(this.EndGetAsyncData);
AddOnPreRenderCompleteAsync(bh, eh);

Can just be written as:

AddOnPreRenderCompleteAsync(BeginGetAsyncData, EndGetAsyncData);
David Taylor wrote re: Async Pages in ASP.NET 2.0 - some results
on 01-11-2005 10:14 PM
Plus or we can alternatively use anonymous methods to do something like this:

void Page_Load()
{
MyService service = new MyService(); // web service

AddOnPreRenderCompleteAsync(
delegate (Object src, EventArgs e, AsyncCallback cb, Object state) {
return service.BeginMyMethod("some param", cb, state);
},
delegate(IAsyncResult ar) {
string res = service.EndMyMethod(ar);
Response.Write(res); // Do something with the output
}
);
}
Fritz Onion wrote re: Async Pages in ASP.NET 2.0 - some results
on 01-12-2005 3:33 AM
David - good point, this is an ideal case for anonymous delegates since the function is a one-liner pass off to the Web Service proxy. I like it!
Carlo Folini wrote re: Async Pages in ASP.NET 2.0 - some results
on 02-01-2005 6:29 AM
Hi,
do you know a way to call another webservice asyncronously after the first?

Thnx
Onion Blog wrote Async tasks in ASP.NET 2.0
on 02-14-2005 4:33 AM
Crasch wrote Async asp.net 2.0
on 02-18-2005 7:40 AM
Sentient thoughts about .NET wrote ASP.NET Threading Nasties.
on 02-24-2005 1:30 AM
Sentient thoughts about .NET wrote ASP.NET Threading Nasties
on 02-24-2005 1:31 AM
K. Scott Allen wrote The ThreadStaticAttribute
on 03-10-2005 8:01 PM
Rick Strahl wrote re: Async Pages in ASP.NET 2.0 - some results
on 05-16-2005 2:00 AM
Not quite sure I understand how this can possibly work. Wouldn't the Web Service BeginHelloWorld use a thread from the ThreadPool as well? WebRequest.BeginGetResponse still uses an AsyncCallback delegate as does the Web Service itself async method itself?
Fritz Onion wrote re: Async Pages in ASP.NET 2.0 - some results
on 05-16-2005 5:53 AM
Good question Rick - yes, the Web Service proxy BeginHelloWorld method will use a thread from the I/O Thread pool (a distinct threadpool used for I/O completion port calls). Because IIS 6.0 uses the Worker thread pool to service requests, this technique successfully offloads request threads to the I/O threadpool which is not taxed like the worker threadpool under heavy load. Note that this is not true in Server 2000 (IIS 5) where I/O threads are used to service requests by default since they are responding to I/O requests issued by IIS over named pipe connections.
pb wrote asynch in 1.0
on 05-18-2005 12:57 PM
Is there a way to do this in ASP .NET 1.1 without writing a custom service, i.e. completely have the code contained for doing so in the web form page as it is above?
Fritz Onion wrote re: Async Pages in ASP.NET 2.0 - some results
on 05-31-2005 3:44 AM
Not really, you have to write a custom handler in 1.1 (or custom Page base class). I presented several examples of how to do this in my June 2003 MSDN article if you're interested:
http://msdn.microsoft.com/msdnmag/issues/03/06/Threading/default.aspx
pb wrote re: Async Pages in ASP.NET 2.0 - some results
on 06-07-2005 12:46 PM
Yeah, I had found that URL via google but couldn't make any sense out of it. I ended up using an asynchronous web service call which works well enough so far.
Impersonation Failure wrote ASP.NET Performance tuning
on 06-10-2005 2:29 AM
Impersonation Failure wrote ASP.NET Performance tuning
on 06-10-2005 2:35 AM
Wintellog wrote re: A Perfect Start to a Perfect Day
on 06-17-2005 7:51 AM
UGbLog di Pierre Greborio wrote Pagine asincrone in ASP.NET 2.0
on 07-19-2005 8:26 AM
Chris' Comments wrote Thinking asynchronously in .NET
on 07-27-2005 9:42 AM
shreeman dash wrote re: Async Pages in ASP.NET 2.0 - some results
on 10-11-2005 1:59 AM
Hi fritz,

I founbd that there is an odd behavior in async pages that is in the sample provided by Jeff Proise here
http://msdn.microsoft.com/msdnmag/issues/05/10/WickedCode/default.aspx
The Page_PreRenderComplete is get called twice.

Any idea for this behavior.???

Thx
shreeman
shreeman wrote re: Async Pages in ASP.NET 2.0 - some results
on 10-13-2005 12:47 AM
Forgot to add one more point .The behavior didn't happens while using asynctask.

the follwing ll get called once :-

protected void Page_PreRenderComplete(object sender, EventArgs e)
{
GridView1.DataSource = _reader;
GridView1.DataBind();
}
while for sync pages the same get called twice thus it seems asynctask is more preferrable.
shreeman dash wrote re: Async Pages in ASP.NET 2.0 - some results
on 10-15-2005 3:59 AM
Seesm i got the reason for the behavior it looks like the fact that the page_prerender automatically hookedup when i m declaring the Async attribute for the page.And since i ve wired up the event myself too the same was got called twice.
Fritz Onion wrote re: Async Pages in ASP.NET 2.0 - some results
on 10-15-2005 4:35 AM
Ah yes, common problem. And now that AutoEventWireup is set to true by default, it's easy to do.
David Foderick's Blog - OnMaterialize() wrote Beta Exam 70-528 Study links
on 10-24-2005 9:01 PM
Here are the resources I am studying for the Microsoft ASP.NET 2.0 Web exam.
Skills measured by exam...
Some Assembly Required wrote Async Pages, Async Tasks, and AsyncTimeout
on 11-04-2005 3:41 PM
Saumin wrote re: Async Pages in ASP.NET 2.0 - some results
on 11-22-2005 12:03 PM
i am looking to fire an asynchronous call to a web service programmatically. is that possible? also i am calling a web service method which jsut accepts arguments and does its own processing...and does nt return anything...so in this case do i need the EndGetAsyncData method? also what happens when the user closes the browser after firing the Begin mehtod? does the webservice continue processing?

thanks,
Torey wrote re: Async Pages in ASP.NET 2.0 - some results
on 11-24-2005 6:07 PM
It certainly looks like a nice enhancement, however I have not been able to use this. Since the async web service calls are using a different thread it does not pass the correct identity to the web service. In fact if you check the identity in the service after it has been called asynchrously you will see that it is not the identity of the person requesting the page. If your service requires that the proper identity be passed on your are forced to use synchronous processing. Has anyone been able to use this new async method and still get the proper identity passed on to the service?
Alex wrote re: Async Pages in ASP.NET 2.0 - some results
on 01-30-2006 6:00 PM
Fritz, what did you use to benchmark your applications, in particular to get the:

distinct threads used: X ammount
Fritz Onion wrote re: Async Pages in ASP.NET 2.0 - some results
on 02-01-2006 12:57 AM
Alex - it was a custom stress utility that I wrote. The threads were identified by returning the threadids from the requests.
Alex wrote re: Async Pages in ASP.NET 2.0 - some results
on 02-17-2006 1:32 PM
Fritz, did you notice that when the webservice is in the same asp.net application, the webservice method accessed from BeginGetAsyncData() is on the same thread as the BeginGetAsyncData() while the EndGetAsyncData() is on a different thread. Because the webservice uses the asp.net thread, as long as the webservice is running, an asp.net thread is being used.
David wrote re: Async Pages in ASP.NET 2.0 - some results
on 03-01-2006 9:55 AM
Is there a way to return a Null or empty IAsyncResult? If there is an error in the web service call what do you return?
Pat wrote re: Async Pages in ASP.NET 2.0 - some results
on 06-25-2006 8:14 PM
Fritz - I have been exploring all your articles/blogs on async techniques in 1.1. Especially your article titled- "Use Threads and Build Asynchronous Handlers in Your Server-Side Web Code". In this article, I learned that by using the common .net async pattern, the "BeginInvoke" of a delegate, will use a thread from the same pool of threads which are servicing your asp.net requests. Therefore under high load situations you may run out of threads. Does this make the well-known asynchronous delegate technique something to stay away from?

My issue is that I am writing a web service in 1.1, that needs to make multiple requests to several other objects to retrieve data. I was going to handle this by creating some business objects that have the ability to be called asynchronously using the begininvoke delegate technique. Due to the information that I have learned in your articles (including this one), I am now worried that my simple technique using the "BeginInvoke" will no longer suffice? Did I understand your message from this article correctly?
verinder wrote re: Async Pages in ASP.NET 2.0 - some results
on 02-24-2007 7:48 PM
Hi,
Nice Article, I am having a different problem. I Have Wizard Control on a Page which mostly does Syncronus call.Now On Next Button click I am using a Business Entity to Make a Rule Call..While rule call is Executing I want to show a Progress Bar Under the Same Wizard Step using some Animation control which stops animation as soon as Rule call returns. Rule call is caled using COM Objects which inturn ques the call to MSMQ Which doesn't take any CPU .

Now I cannot set the Page Async =true for my Syncronus Task. But when there is a Asyncronus task initiated i do want the callback to return to Animaction control in page which will stop the animation and hide the control itself and show the normal page...

What to Do???
Verinder
Pete Kusnick wrote re: Async Pages in ASP.NET 2.0 - some results
on 05-30-2007 6:51 AM
It should be up on the default.htm production page soon. I thank the author because his was the only site that had a working solution.
Pete Kusnick wrote re: Async Pages in ASP.NET 2.0 - some results
on 07-11-2007 10:18 AM
The Waiting list section is the web service client I developed. One problem we experience is occassionally none of the data is rendered, only the text: Waiting List Candidates.

Cale Teeter wrote re: Async Pages in ASP.NET 2.0 - some results
on 07-12-2007 7:17 AM
After ASP.NET registers a delegate to be called back when the work is complete, and releases the request thread back to the pool what happens to the HttpApplication object? I am trying to understand when using an Asynchronous HttpHandler, is there a thread still consumed per request or does ASP.Net have some sort of table of current requests waiting for callbacks? I am trying to determine if I loaded the server down with thousands of requests, all calling an async handler that takes time to complete, where will the next bottleneck show up and how ASP.Net manages the waiting requests. Thanks.
Ishfaq Hussain wrote re: Async Pages in ASP.NET 2.0 - some results
on 09-10-2007 11:53 PM
Does the ASP.Net page supports multiple asynchronous requests. For example, i have button on the page which performs some database operation, if i click the button more than once then it should return the result as many times as i have clicked the button.
It is possible using AJAX page request manager and adding the asynchronous requests to queue.
But the results of all clicks are returned sequentially. which means the requests are queued and each request executes after the previous one has been finished. What if i want to execute the requests in parallel.

Add a Comment

(required)