Understanding asynchronous programming in ASP.NET

I recently posted about asynchronous programing patterns in .NET. Like .net framework, ASP.NET has supported asynchronous programming since 1.x. C# and VB.NET introduced the new async and await keywords in the upcoming Visual Studio 11. This is also supported in the upcoming ASP.NET 4.5. On the other hand, there is currently an open source project from the ASP.NET team called SignalR. It uses synchronous programming model to support long running comet-like request in ASP.NET. SigalR can be used in ASP.NET 4.0 today. The purpose of this post is to talk about a little bit of history so that we can put new projects/features in perspective.

In asynchronous programming, the callee will create another thread to handle the execution and return immediately the the caller. The caller can go on with another task, or wait, join, or be called back from the callee. So one may ask what does asynchronous programming have to do with ASP.NET. Which is the caller and which is the callee?

It turns out that the caller is ASP.NET and the callee is the asynchronous HttpHandler. When IIS calls into ASP.NET, ASP.NET immediately posts the request to the CLR ThreadPool and returns a pending status to IIS. This frees the IIS thread so that it can be used to handle static contents. In the request is handled by an IHttpHandler in ASP.NET, a thread will handle the entire life cycle of the request. If the request is handled by an IHttpAsyncHandler, ASP.NET will call the BeginProcessRequest method of the handler and return the thread to the thread pool as soon as the method exists. If the handler simply creates another thread in the BeginProcessRequest method and immediately returns, there is really no benefits since we are simply replace one thread with another. If the handler make an asynchronous call to another class that is IO blocking and immediately returns, there is real benefit because the ASP.NET thread is no longer blocked by the IO and can be returned to the thread-pool to server other requests. Even though there is not a thread associated with the request, the connection remains open until the call-back delegate passed into the BeginProcessRequest method is called. This powerful method allows an asynchronous ASP.NET application to handle far more requests than synchronous application if IO blocking is a concern. On the other hand, it is possible for an application to keep a connection open for a long time with allocating a thread, and allocates thread only on events. That is why SignalR can work today. What is the cost to keep those connections open? The TCP/IP stack needs to keep connection info. IIS and ASP.NET also need to keep the context info. That’s say we need in average 5kB to keep a connection open. Then 1GB of memory would allow us to keep 200,000 connections open. This is unusually large number for synchronous asp.net applications. That is why SignalR implementations often configure much larger number of connections than the 5000 default in IIS7.

References:

[1] Performing Asynchronous Work, or Tasks, in ASP.NET Applications.

[2] ASP.NET Thread Usage on IIS 7.5, IIS 7.0, and IIS 6.0

No Comments