I have been reading a lot of on ASP.NET Web application performance enhancement and found it some how tedious job to learn ASP.NET Multi threading (as a beginner). So I thought i will share briefly about all the things required to have a better understanding of the concepts and how things work. This post will start with some introduction to the Multi threading in general, then CLR Thread pool and finally to some great resources for learning about ASP.NET request life cycle and how does multi threading works with ASP.NET.
C# 5 is out with the new Async feature. The basic idea is to reduce the complexity of the Asynchronous code that developers have been writing. A good article about C# Async can be read here. But the way Asynchronous pages in Web Forms works is somehow different and can be confusioning. For those writing Asynchronous code for the first time are not quite sure how it all works. The confusion is about whether to use Thread Pool, Asynchronous delegates, Create our own threads or what???.
This post is a brief overview of how things work and links the important resources and information available.
OK, so most of us know that we have delegate types in .NET that help us write us code and invoke it asynchronously using the BeginInvoke and get the result of this async operation using EndInvoke. Here is a question on StackOverflow about what's the difference between Invoke() and BeginInvoke(). Jon Skeet writes a very good answer to that. And ofcourse, you know who Jon Skeet is :)
So now we know, we have a BeginInvoke to run our code on a new Thread (This thread is used from the ThreadPool). If you are new to Multithreading then here are a few things that you should know and memorize them:
- Only a single thread can execute on a CPU at any time.
Now a days we are having multi core machines, they have multiple no. of CPU and hence we can run more threads (Threads executing at a single instant of time equals the no. of CPU). So for e.g. a machine with 10 CPU can run 10 threads at a instant of time.
When we have many threads running, it is totally dependent on Operating System so as to which Thread should execute first. The threads will perform what is called, Context Switching and then the thread selected will get the CPU to execute. The complexity of this increases with multi core as any thread can execute on any available CPU.
What? OS controls all this. Not to get sad, We have ways to control the Thread priority and type (Foreground or background). After .NET version 4.0 release, Task and Task Parallel Library is the recommended way to do Async programming.
We have many classes available in the .NET Framework to get things done quickly without having to write a lot of code. Read this answer to get familiar with them and most importantly when to use what?
Usually the thread creation is not an easy task. It takes a lot of resources and allocations to do so. So It is not usually recommended to create our own threads unless required (After reading the answer at the link you must have an idea of when you use what?).
This was the main reason behind the existence of the Thread Pool. A Thread Pool is basically a collection of Threads so that we don't have to create thread more often. A brief overview of how Thread Pool works:
We have a thread pool with a set of Worker Threads and IO Threads. Worker threads are what used for common time consuming tasks in code and IO Threads are used to carry out Asynchronous IO operations.
.NET is managed code and uses Windows API underneath. There is a very important difference to note here. There are two things: ThreadPool.QueueUserWorkItem (QUWI) and Asynchronously invoking a delegate using BeginInvoke and EndInvoke. Both of them can be used and they will use the Worker threads to do the work. But there is difference in performance of the two. The QUWI has a better performance then the Begin/End Invoke way of calling the code. The reason being "Asynchronous delegate does use Thread Pool thread but it goes through calling .NET Remoting stack to get to use the Thread Pool thread."
ThreadPool class implements certain algorithm that it uses to determine how many threads needs to be maintained in the Thread pool. Any thread created by the thread pool can be re-used to do any other work. If many threads are created by the thread pool and the load suddenly decreases, then we have many idle threads. And what will happen now is "Context Switching". More the number of threads, more is the context switching and thus performance issue. To avoid such conditions the algorithm implemented in thread pool will destroy threads after idle time limit is reached. All this is specific to thread pool implementation. Jeffrey Richter in MSDN magazine about CLR Thread Pool.
IIS processes ASP.NET request and this whole request is very nicely described by Abhijit in his article on Beginner’s Guide: How IIS Process ASP.NET Request. For a more deep and low level understanding of ASP.NET Request life cycle by Rick Strahl in the post A low-level Look at the ASP.NET Architecture. Then follow the post here to learn more about ASP.NET Thread Usage on IIS 7.5, IIS 7.0, and IIS 6.0. Then you should read another related post about Performing Asynchronous Work, or Tasks, in ASP.NET Applications. Asynchronous web pages is a good start for a simple sample application. Although the particular article focus on ASP.NET 2.0 but it remains the same in .NET 3.5 Framework.
You should now have a better understanding of how to make Asynchronous pages. Also here is a link to a video about Async in ASP.NET.
Hope you find the post useful.
Happy Reading :)