These notes follow on from a Jeff Prosise talk on this subject at DevWeek 2007.
Asynchronous ASP.NET Programming – Jeff Prosise
Most sites that access external data (databases, web services etc) are written are done so in a synchronous way, and when the number of users increases the site can suddenly slow right down to a crawl, or even fail totally.
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 Web service or queries a 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.
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.
ASP.NET 2.0 however supports asynchronous pages.
When the request arrives, it's assigned a thread by ASP.NET. The request begins processing on that thread, but when the time comes to hit the external data, the request launches an asynchronous request and returns the thread to the thread pool. When the query completes, the asynchronous request calls back to ASP.NET, and ASP.NET grabs another thread from the thread pool and resumes processing the request.
While the query is outstanding, zero thread pool threads are consumed, leaving all of the threads free to service incoming requests. A request that's processed asynchronously doesn't execute any faster. But other requests execute faster because they don't have to wait for threads to become free. Requests incur less delay in entering the pipeline, and overall throughout goes up.
Asynchronous HTML Handler Pages
The second asynchronous programming model featured in ASP.NET is the asynchronous HTTP handler. An HTTP handler is an object that serves as an endpoint for requests. Requests for ASPX files, for example, are processed by an HTTP handler for ASPX files. Likewise, requests for ASMX files are handled by an HTTP handler that knows how to deal with ASMX services.
You can extend ASP.NET to support additional file types by writing custom HTTP handlers. But even more interesting is the fact that you can deploy custom HTTP handlers in ASHX files and use them as targets of HTTP requests. This is the proper way to build Web endpoints that generate images on the fly or retrieve images from databases. You simply include an <img> tag (or Image control) in the page and point it to an ASHX that creates or fetches the image. Targeting an ASHX file with requests is more efficient than targeting an ASPX file because an ASHX file incurs much less overhead at processing time.
By definition, HTTP handlers implement the IHttpHandler interface. Handlers that implement that interface do their processing synchronously
HTTP handlers don't have to be synchronous. By implementing the IHttpAsyncHandler interface, which itself derives from IHttpHandler, an HTTP handler can be asynchronous. When used correctly, an asynchronous handler utilizes ASP.NET threads more efficiently. This is done in the same manner as an asynchronous page. In fact, asynchronous pages leverage the asynchronous handler support that predated asynchronous pages in ASP.NET.
See http://msdn.microsoft.com/en-gb/magazine/cc163725.aspx and http://msdn.microsoft.com/msdnmag/issues/07/03/WickedCode/default.aspx for more information.