ADO.NET 4.5 Async Data Reader and IAsyncHttpHandler
22 November 2012 - ASP.NET
Having written quite a few lines of Silverlight (RIP!) as well as asynchronous WPF code over the last two years, I have started to appreciate the Don't-Create-A-Thread-For-Everything approach. On the server side node.js is definitely one of the frameworks to thank for the popularity boost and lately the async-await feature in C# 5 has taken things to a new level.
Asynchronous Web Services
When ASP.NET processes a request it assigns a thread from the thread pool to this request. The usual web service scenario then connects to a database by opening a connection (ADO.net will helpfully assign a connection from a pool), issues a select and then reads the result one by one from the database. In the synchronous world the executing thread waits for the operation to finish, which generally implies idling the thread and reducing CPU utilization. The goal here is to allow that execution thread to handle a different request (or whatever other task there is to do). Essentially it is a form of cooperative multithreading.
First the synchronous implementation of our simple person table reader:
Asynchronous Version using async/await
In the second version, I made everything asynchronous by calling the corresponding Async() methods and awaiting the result. I also inherited from HttpTaskAsyncHandler instead of implementing IHttpHandler. Finally, knowing async await in C# 5.0 can help with increasing performance even further: By default an await captures the current ExecutionContext and dispatches to the same thread when the asynchronous operation is done. This therefore has some overhead and can be turned off by calling .ConfigureAwait(false) on the task returned by the async function call and finally awaiting it. I also have to issue a warning here though: If you are using .ConfigureAwait(false) your static context variables will possibly not match after a return from another thread. E.g. HttpContext.Current will possibly refere to another request. So be sure to not reference static variables (you should not anyhow!) and watch those libraries you are using for that.
So here the asynchronous version of the sample above:
As you can see, the PerformRequest method now returns a task and is sprinkled with await calls, but conceptionally the flow of the function stays pretty much the same.
Finally, I have to mentioned that i have only tested this in a localhost / LocalDB environment and one thing that obviously became apparent is that there is, of course, overhead associated with the async function call. Because there is no latency to the LocalDB SQL server and the requests are repeated over and over again, the asynchronous functions could actually complete pretty close to synchronously therefore rendering the task (no pun intended) useless. Further, realize that the amount of asynchronity will depend on your use case: It might be beneficial to execute the Open and ExecuteCommand operations asynchronously as they usually will require more time than the asynchronous MoveNext which could be more efficient to execute synchronously.
As always, benchmark your results and find the right tradeoffs!