Using Task or async/await in IHttpAsyncHandler
Asked Answered
G

6

18

Since the very begining of writing ASP.NET applications when I wanted to add a threading there are 3 simple ways I can accomplish threading within my ASP.NET application :

  • Using the System.Threading.ThreadPool.
  • Using a custom delegate and calling its BeginInvoke method.
  • Using custom threads with the aid of System.Threading.Thread class.

The first two methods offer a quick way to fire off worker threads for your application. But unfortunately, they hurt the overall performance of your application since they consume threads from the same pool used by ASP.NET to handle HTTP requests.

Then I wanted to use a new Task or async/await to write IHttpAsyncHandler. One example you can find is what Drew Marsh explains here : https://mcmap.net/q/670140/-using-an-ihttpasynchandler-to-call-a-webservice-asynchronously

My guess is that using Task or async/await still consume the thread from the ASP.NET thread pool and I don't want for the obvious reason.

Could you please tell me if I can use Task (async/await) on the background thread like with System.Threading.Thread class and not from thread pool ?

Thanks in advance for your help.

Thomas

Gavette answered 10/2, 2012 at 9:20 Comment(3)
It's not obvious to me that consuming threads from the same pool hurts performance. Are you sure that's the case?Demagogic
In fact I depends. If I understand it well there is no more limitations on threads in asp.net threadpool in framework 4. So if there is a need to handle more requests, new threads are injected into the thread pool. Before framework 4 there was a limitation on threadpool as I remember.Farm
tl;dr for future visitors, the canonical solution at this point (as mentioned by Stephen in his answer) is to just use HttpTaskAsyncHandler.Decrypt
G
7

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

Gavette answered 24/2, 2012 at 10:19 Comment(0)
G
22

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 implementation
public 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.

Grapher answered 10/2, 2012 at 14:54 Comment(11)
Thank you for your response. I didn't take time to dive into AsyncEx library yet but just to rephrase what you said...if I use Taks or async/await I won't be consuming ASP.NET thread for the background work. Another question to satisfy my curiosity would be, is there an easy way to check/monitor that behaviour ?Farm
I don't know of a way to monitor it.Grapher
An ASP.NET request thread kicks off the request, and it starts downloading using the WebClient. -----well only for IO operations which the drivers do the work and then later it is tranferd to a threadpool thread. if it doesnt use IO operations , there will(!!!!) be a background thread to do the operations.Indefectible
@RoyiNamir: A background thread is only used if you explicitly request one (e.g., Task.Run). With only a handful of exceptions (e.g., asynchronously reading from a MemoryStream), the BCL will never do this on your behalf. So it's really easy to avoid unnecessary background threads - just don't use them!Grapher
You wrote : "The really cool thing about async is that it doesn't take any threads while doing the background operation:".......and Im just saying that it does take a background thread , unless you use IO asynchronous methods (e.g. FileStream.BeginRead ). this will not tie a thread ....while downloading regularly --- will tie a thread ( background thread)Indefectible
Not sure what you're getting at. A WebClient download using DownloadDataTaskAsync does not take a background thread.Grapher
Correct. but your statement ["The really cool thing about async is that it doesn't take any threads while doing the background operation:"] --- implies that it is always happening , and that it not true.I'm saying it happens ONLY IF YOU USE ASYNCHRONOUS METHODS ( e.g. DownloadDataTaskAsync ). in other words , if you don't use the designated IO asynchronous methods - it WILL USE A BACKGROUND THREAD. please reply.Indefectible
async - by itself - will not use any background threads. Unless you explicitly tell it to by calling Task.Run.Grapher
I suggest you post your own question on SO on this matter.Grapher
@StephenCleary the link i referenced you to , is an answer to my question.Indefectible
Its important to note that you can no longer rely on HttpContext.CurrentDaughtry
G
7

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

Gavette answered 24/2, 2012 at 10:19 Comment(0)
A
3

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.

Alesandrini answered 15/2, 2012 at 16:43 Comment(2)
Andrés, That's very interesting point. So using async/await (or Task) consumes in the firts place the ASP.NET threadPool thread, unless the underlying implementation uses it's own threadpool. So doing for example expensive calculation in the AsyncHandler will use another thread but it will be taken from ASP.NET ThreadPool? Can you confirm it ? Do you have any links where I could read further ?Farm
See my post below, tried to answer here but run out of characters... :)Alesandrini
N
1

The Parallel Extensions team has a blog post on using TPL with ASP.NET that explains how TPL and PLINQ use the ASP.NET ThreadPool. The post even has a decision chart to help you pick the right approach.

In short, PLINQ uses one worker thread per core from the threadpool for the entire execution of the query, which can lead to problems if you have high traffic.

The Task and Parallel methods on the other hand will adapt to the process's resources and can use as little as one thread for processing.

As far as the Async CTP is concerned, there is little conceptual difference between the async/await construct and using Tasks directly. The compiler uses some magic to convert awaits to Tasks and Continuations behind the scenes. The big difference is that your code is much MUCH cleaner and easier to debug.

Niko answered 16/2, 2012 at 14:39 Comment(0)
A
1

Another thing to consider is that async/await and TPL (Task) are not the same thing.

Please read this excellent post http://blogs.msdn.com/b/ericlippert/archive/2010/11/04/asynchrony-in-c-5-0-part-four-it-s-not-magic.aspx to understand why async/await doesn't mean "using a background thread".

Going back to our topic here, in your particular case where you want to perform some expensive calculations inside an AsyncHandler you have three choices:

1) leave the code inside the Asynchandler so the expensive calculation will use the current thread from the ThreadPool. 2) run the expensive calculation code in another ThreadPool thread by using Task.Run or a Delegate 3) Run the expensive calculation code in another thread from your custom thread pool (or IOCP threadPool).

The second case MIGHT be enough for you depending on how long your "calculation" process run and how much load you have. The safe option is #3 but a lot more expensive in coding/testing. I also recommend always using .NET 4 for production systems using async design because there are some hard limits in .NET 3.5.

Alesandrini answered 16/2, 2012 at 14:56 Comment(0)
F
0

There's a nice implementation of HttpTaskAsyncHandler for the .NET 4.0 in the SignalR project. You may want to ckeck it out: http://bit.ly/Jfy2s9

Fruge answered 21/4, 2012 at 12:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.