Take the community feedback survey now.

Magnus Rahl
Jan 20, 2012
  28002
(1 votes)

Async Pages part 2: How to use asynchrony in your Pages

This is a follow-up on the post How async pages may save your (server’s) life. In that post you may learn a bit more about how ASP.NET processing works and how your site can become incredibly slow even though your server isn’t busy (in the CPU load sense).

That post also described how you could get pre-.NET 4 sites to process a little better by setting the system.web/applicationPool maxConcurrentRequestsPerCPU attribute in your aspnet.config.

In this post we put some code to work to really solve the problem of requests blocking the queue.

Use async to change mode of processing

So how do you really solve the blocking problem? Say hello to the async Page attribute and it’s friends.

By putting async=”true” in your aspx:s Page directive you signal to ASP.NET that it can do asynchronous processing. What ASP.NET will then do is process your page to the PreRender stage and then look if you registered any async tasks. If you did, the thread will put your request back on a queue and go process another request. When your async task completes your request will be picked up by a thread again (not necessarily the same thread) and be processed to completion and the result sent to the client.

Async processing of UserControls

You can do async processing in UserControls as well as Pages as long as the hosting Page has its async attribute set to true.

Example UserControl using async processing

Consider the following UserControl used for demonstrating the async calls:

<%@ Control Language="C#" AutoEventWireup="false" CodeBehind="UrlStatus.ascx.cs" Inherits="EPiServer.Templates.AsyncControls.UrlStatus" %>
<p>
    Request to <%= Server.HtmlEncode(Url) %> started on thread <%= StartThread %>
    and completed on thread <%= EndThread %> in <%= ExecutionTime %>ms
    with status code <%= StatusCode %>. This was rendered on thread <%= System.Threading.Thread.CurrentThread.ManagedThreadId %>.
</p>

As you can see it has a number of properties of which Url is a public property set from the page. StartThread, EndThread, ExecutionTime and StatusCode are used to store values demonstrating the async process.

Setting up the async processing

In the codebehind are of course the mentioned properties and also some fields used in the following methods. First the OnLoad method override:

protected override void OnLoad(EventArgs e)
{
    base.OnLoad(e);
    // Register an asynchronous operation which will begin after PreRender
    // stage and complete before PreRenderComplete
    Page.AddOnPreRenderCompleteAsync(BeginAsyncOperation, EndAsyncOperation);
}

This simply says: When PreRender is done, initiate the asynchronous processing by calling BeginAsyncOperation and later use EndAsyncOperation to process the result. The parameters are actually delegates, but that is inferred from the method names by the compiler.

Performing the async call

The BeginAsyncOperation looks like this:

private IAsyncResult BeginAsyncOperation(object sender, EventArgs e, AsyncCallback cb, object state)
{
    // Save the current thread and time for demonstration purposes
    StartThread = System.Threading.Thread.CurrentThread.ManagedThreadId;
    _requestStartTime = DateTime.Now;
    _request = (HttpWebRequest)WebRequest.Create(Url);
            
    // With this return the runtime may stop processing the request and use the
    // thread to service another request until this operation completes
    return _request.BeginGetResponse(cb, state);
}

The StartThread property used in the view is set and the time is recorded in a member. As you can see, another member, _request, is used to store the request we create for the URL in the Url property. The reason for this is that we need it later to retrieve the result (the response) when the async call completes. That is done in EndAsyncOperation which looks like this:

private void EndAsyncOperation(IAsyncResult ar)
{
    // When this method is called, it means that the asynchrounous
    // call is completed. Record the thread and time again
    EndThread = System.Threading.Thread.CurrentThread.ManagedThreadId;
    var requestEndTime = DateTime.Now;
    var response = (HttpWebResponse)_request.EndGetResponse(ar);           
            
    // Calculate the execution time and set the status code for rendering
    ExecutionTime = (requestEndTime - _requestStartTime).TotalMilliseconds;
    StatusCode = response.StatusCode.ToString();
    // Sometime after this control is handled to a thread that does
    // the rendering of the page (could be different from this thread)
}

Again, some of the properties used in the view are set. And as you can see the IAsyncResult object we returned from BeginAsyncOperation is passed back to us here. But when this method is called we are no longer necessarily running on the same thread (more probably we’re not). We use the _request field to retrieve the response and set the final property to display to it’s status code.

Understanding what happened

To try to make sense of this, Imagine the UserControl is registered in a Page (which has async=”true”) and used like this:

<h2>Async web request test</h2>
<Async:UrlStatus runat="server" Url="http://world.episerver.com/" />

The result can look like this:

async_web_request_test

Note that at least the start and end thread may sometimes be the same, it’s simply a matter of which thread is free when the async call completes.

Now, remember that when thread 7 had finished it’s setup of the async call it left the request sitting in memory and went to pick up another request (if there was one), then another and so on.

At some time our code’s request to EPiServer World completes and is handed over from the native code IP sockets or whatever, to thread 15 which enters our code and lets us process the result. Then it puts the request back on the queue where thread 8 (which up until now has also been serving other requests) picks up where thread 7 left to render the page and send it to the client.

(A bit) more advanced examples

Move on to the next blog post: Async Pages with databinding and events for more examples.

Jan 20, 2012

Comments

Please login to comment.
Latest blogs
Content modeling for beginners

  Introduction Learning by Doing – Optimizely Build Series  is a YouTube series where I am building  a fictional  website called  TasteTrail , food...

Ratish | Sep 14, 2025 |

A day in the life of an Optimizely OMVP - Enhancing Search Relevance with Optimizely Graph: Synonyms and Pinned Results

When building search experiences for modern digital platforms, relevance is everything. Users expect search to understand their intent, even when...

Graham Carr | Sep 14, 2025

Optimizely CMS and HTML validation message: Trailing slash on void elements has no effect and interacts badly with unquoted attribute values.

When using the W3C Markup Validation Service, some annoying information messages pop up because Optimizely CMS adds the trailing slash to...

Tomas Hensrud Gulla | Sep 14, 2025 |

Turbocharge your strings - a case of display channels

When doing a routine performance test, during a CMS 12 upgrade, I was able to achieve 95% performance improvement. Let's look at SearchValues with ...

Stefan Holm Olsen | Sep 14, 2025 |

Opal Core Concepts

Before you dive into the code, it's crucial to understand the foundational ideas that make Opal tick. Core concepts are consistent across all its...

K Khan | Sep 13, 2025

Optimizely Opal : Reimagining A Utility Sector Use Case

  Introduction Customer engagement through timely and personalized push notifications plays a crucial role in todays Digital First landscape. In th...

Ratish | Sep 12, 2025 |