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.
In Visual Studio 11, C# and VB.NET will gain new keywords like async and await for asynchronous programming. Before we dive into it, I will first give a brief preview on what we have so far.
IAsyncResult pattern has existed since .net 1.x. The class that implements pattern would implement Beginxxx and Endxxx methods.For example, the WebRequest class has BeginGetResponse and EndGetResponse in additional to synchronous GetResponse method. Here is the signature of the BeginGetResponse method:
public virtual IAsyncResult BeginGetResponse( AsyncCallback callback, Object state )
The caller of the method needs to supply a callback function which will be call when the asynchronous operation is over. The callee will usually create another thread to do the actual operation and immediate an IAsyncResult to the caller. Here are the members of IAsyncResult:
Member Description AsyncState An optional application-specific object that contains information about the asynchronous operation. AsyncWaitHandle A WaitHandle that can be used to block application execution until the asynchronous operation completes. CompletedSynchronously A value that indicates whether the asynchronous operation completed on the thread used to call BeginOperationName instead of completing on a separate ThreadPool thread. IsCompleted A value that indicates whether the asynchronous operation has completed.
The caller of Beginxxx method can do one of the following:
- Wait on the AsyncWaitHandle if the caller needs to do processing after the async operation.
- Poll the IsCompleted and AsyncState for completion and possibly the progress.
- Do nothing. Wait for the callback to be called.
- Call the Endxxx method. If it is called before the completion of the async operation, the calling thread will block until the asynchronous operation is complete.
Event-based Asynchronous Pattern
Since .net 2.0, we gain another asynchronous pattern – the Event-based Asynchronous Pattern (EAP). EAP was created to allow a richer event model. The class that implement EAP can raised multiple events during a single asynchronous operation so that the callee does not have to poll the class.
The class that implements EAP uses xxxAsync naming conversion for its asynchronous methods. The class can optionally implements xxxCancelAsync methods or simply a CancelAsync method to allow caller to cancel the asynchronous operation. When a caller calls a xxxAsync method, the method will immediately return. The caller needs to subscribe one of the events exposed by the class for notifications of completion and progress. WebClient and BackgroundWorker are two typical components that use this pattern. For library designers, MSDN has a nice article descripting when to use which pattern.
Task-based Asynchronous Pattern
.Net framework 4.0 introduced yet another way to run asynchronous operation – the Task class in the Task Parallel Library. Although the task parallel library is about parallel execution and is much more than asynchronous programming, the .net framework 4.5 uses it as the base for yet another asynchronous pattern – the Task-based Asynchronous Patterns (TAP). The class that implements TAP would expose xxxAsync methods, just like EAP. However, the xxxAsync methods return Task<TResult>. The callee can then use the members of Task<TResult> to check completion or progress, wait or continue with another operation. This pattern is the basis for the async and await keywords in Visual Studio 11; the methods that support TAP can be marked using the async keywoard and then consumed asynchronously with the await keyword. That is why only methods return Task, Task<TResult> or void (that is, no results) can be marked with async.
Found this MSDN Magazine article that discusses these patterns in more detail: http://msdn.microsoft.com/en-us/magazine/ff959203.aspx.