Using Task or async/await in IHttpAsyncHandler Using Task or async/await in IHttpAsyncHandler asp.net asp.net

Using Task or async/await in IHttpAsyncHandler


This situation is where Task, async, and await really shine. Here's the same example, refactored to take full advantage of async (it also uses some helper classes from my AsyncEx library to clean up the mapping code):

// First, a base class that takes care of the Task -> IAsyncResult mapping.// In .NET 4.5, you would use HttpTaskAsyncHandler instead.public abstract class HttpAsyncHandlerBase : IHttpAsyncHandler{    public abstract Task ProcessRequestAsync(HttpContext context);    IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)    {        var task = ProcessRequestAsync(context);        return Nito.AsyncEx.AsyncFactory.ToBegin(task, cb, extraData);    }    void EndProcessRequest(IAsyncResult result)    {        Nito.AsyncEx.AsyncFactory.ToEnd(result);    }    void ProcessRequest(HttpContext context)    {        EndProcessRequest(BeginProcessRequest(context, null, null));    }    public virtual bool IsReusable    {        get { return true; }    }}// Now, our (async) Task implementationpublic class MyAsyncHandler : HttpAsyncHandlerBase{    public override async Task ProcessRequestAsync(HttpContext context)    {        using (var webClient = new WebClient())        {            var data = await webClient.DownloadDataTaskAsync("http://my resource");            context.Response.ContentType = "text/xml";            context.Response.OutputStream.Write(data, 0, data.Length);        }    }}

(As noted in the code, .NET 4.5 has a HttpTaskAsyncHandler which is similar to our HttpAsyncHandlerBase above).

The really cool thing about async is that it doesn't take any threads while doing the background operation:

  • An ASP.NET request thread kicks off the request, and it starts downloading using the WebClient.
  • While the download is going, the await actually returns out of the async method, leaving the request thread. That request thread is returned back to the thread pool - leaving 0 (zero) threads servicing this request.
  • When the download completes, the async method is resumed on a request thread. That request thread is briefly used just to write the actual response.

This is the optimal threading solution (since a request thread is required to write the response).

The original example also uses threads optimally - as far as the threading goes, it's the same as the async-based code. But IMO the async code is easier to read.

If you want to know more about async, I have an intro post on my blog.


I've been looking for information through internet for a couple of days. Let me sum up what I found until now :

ASP.NET ThreadPool facts

  • As Andres said: When async/await will not consume an additional ThreadPool thread ? Only in the case you are using BCL Async methods. that uses an IOCP thread to execute the IO bound operation.

  • Andres continues with ...If you are trying to async execute some sync code or your own library code, that code will probably use an additional ThreadPool thread unless you explicitely use the IOCP ThreadPool or your own ThreadPool.

But as far as I know you can't chose whetever you want to use a IOCP thread, and making correct implementation of the threadPool is not worth the effort. I doubt someone does a better one that already exists.

  • ASP.NET uses threads from a common language runtime (CLR) thread pool to process requests. As long as there are threads available in the thread pool, ASP.NET has no trouble dispatching incoming requests.

  • Async delegates use the threads from ThreadPool.

When you should start thinking about implementing asynchronous execution ?

  • When your application performs relatively lengthy I/O operations (database queries, Web service calls, and other I/O operations)

  • If you want to do I/O work, then you should be using an I/O thread (I/O Completion Port) and specifically you should be using the async callbacks supported by whatever library class you're using. Theirs names start with the words Begin and End.

  • If requests are computationally cheap to process, then parallelism is probably an unnecessary overhead.

  • If the incoming request rate is high, then adding more parallelism will likely yield few benefits and could actually decrease performance, since the incoming rate of work may be high enough to keep the CPUs busy.

Should I create new Threads ?

  • Avoid creating new threads like you would avoid the plague.

  • If you are actually queuing enough work items to prevent ASP.NET from processing further requests, then you should be starving the thread pool! If you are running literally hundreds of CPU-intensive operations at the same time, what good would it do to have another worker thread to serve an ASP.NET request, when the machine is already overloaded.

And the TPL ?

  • TPL can adapt to use available resources within a process. If the server is already loaded, the TPL can use as little as one worker and make forward progress. If the server is mostly free, they can grow to use as many workers as the ThreadPool can spare.

  • Tasks use threadpool threads to execute.

References


Saying that "0 (zero) threads will be servicing this request" is not accurate entirely.I think you mean "from the ASP.NET ThreadPool", and in the general case that will be correct.

When async/await will not consume an additional ThreadPool thread? Only in the case you are using BCL Async methods (like the ones provided by WebClient async extensions) that uses an IOCP thread to execute the IO bound operation.

If you are trying to async execute some sync code or your own library code, that code will probably use an additional ThreadPool thread unless you explicitely use the IOCP ThreadPool or your own ThreadPool.

Thanks,Andrés.