Don’t use the ThreadPool in ASP.NET

Jun 17, 2007

I’ve always been a big fan of using the ThreadPool for asynchronous execution, but in ASP.NET it is not the best approach for multi-threading. I’m not writing about when threading is appropriate and the impact of multi-core or dual core machines when doing threading, but point out that the ThreadPool is not the best choice for ASP.NET applications.

ThreadPool is easy

The reason why I like the ThreadPool is because it is managed for me and it is very easy to use and it only takes one line of code to execute a method in a new thread by using the QueueUserWorkItem method.

System.Threading.ThreadPool.QueueUserWorkItem(SomeMethod);

Private void SomeMethod(object stateInfo)
{
    // Execute something...
}

You can also send a parameter easily like so:

System.Threading.ThreadPool.QueueUserWorkItem(SomeMethod, variable);

That variable then gets transferred to the stateInfo parameter of SomeMethod where you can then cast it from object to whatever data type it is.

Private void SomeMethod(object stateInfo)
{
    int number = (int)stateInfo;
    // Execute something...
}

The ThreadPool in ASP.NET

You can use the ThreadPool in exactly the same way in ASP.NET and it works just as you would expect. The problem is not in the ThreadPool itself but in what else ASP.NET uses it for at the same time. ASP.NET is multi-threaded by design and it uses the ThreadPool to serve pages and content mapped to the ASP.NET ISAPI filter.

If you also use the ThreadPool, then ASP.NET has fewer threads to utilize and requests are put on hold until the pool returns a free thread. This might not be a problem for a low traffic site, but more popular sites can get into trouble. Low traffic sites can get into trouble if they use the ThreadPool a lot.

Don’t use it

Whether you work on a low or high traffic site, there really is no reason to use the ThreadPool when you can create new threads almost as easily that doesn’t disturb the pool. Here is an example that uses an anonymous method as a delegate:

System.Threading.ThreadStart threadStart = delegate { SomeMethod(variable); };
System.Threading.Thread thread = new System.Threading.Thread(threadStart);
thread.IsBackground = true;
thread.Start();

That’s four lines instead of one, but that’s a low price to pay. You could always create a helper method to call whenever you want to start a new thread.

public static void StartBackgroundThread(ThreadStart threadStart)
{
  if (threadStart != null)
  {
    Thread thread = new Thread(threadStart);
    thread.IsBackground = true;
    thread.Start();
  }
}

Then just call it in one line like so:

StartBackgroundThread(delegate { SomeMethod(variable); });

Word of caution

The ThreadPool is managed by the CLR, which provide a level of control that a normal thread doesn’t get. By using an un-pooled thread you also have the ability to do much more harm if you don’t know what you are doing.

You have the ability to stop the AppPool from being recycled and the application from being stopped if you aren’t careful. For instance, if you set the IsBackground property to false, it will exist in the foreground and can make it difficult for the application to recycle or restart. However, the example shown above is not doing that, so don’t worry about damaging your application by using it.

* $4.95/month BlogEngine.net Hosting – Click Here!

Comments (11) -

Michael Nemtsev
Michael Nemtsev Russia
6/17/2007 7:42:31 PM #

Good description, Mads.
The another description of this issue is into MSDN Magazine msdn.microsoft.com/.../default.aspx

Claus
Claus Denmark
6/17/2007 7:59:09 PM #

Almost every ware in the framework where you may encounter a method beginning with “Begin******” it’s more that likely that this method do has something fiddling around in those pools.

Michael Nemtsev
Michael Nemtsev Russia
6/18/2007 5:56:55 AM #

Yep, right Claus.
Except the Remoting where the IO ports are used

mcgurk
mcgurk United States
6/18/2007 12:55:07 PM #

Wait a minute... the threadpool is controlled by the CLR, not the ASP.NET worker process.  Your points otherwise are valid and something I haven't really considered before.  Now that I think about it, since the threadpool doesn't guarantee anything about execution of worker items, it could easily end up badly if you queue a work item.  The threadpool might dispatch a thread immediately to service the request, or it may idle the request while one single thread serves every request sent to the threadpool.  And since you're not only competing with other processes on the same machine but the ASP.NET worker process in which your code is executing, that could easily go south on you quickly.  Fken saved.

Mads Kristensen
Mads Kristensen Denmark
6/18/2007 1:07:35 PM #

mcgurk, you are right about the CLR controlling the threadpool. It slipped...

NinjaCross
NinjaCross Italy
6/18/2007 10:13:32 PM #

I completely agree with you about the problems related to the performances leaks involved in using the ThreadPool.
I found some similar issues on an email scheduler/spooler I developed some months ago, and to solve them I had to use a Thread instance instead cause no other solutions solved the problem.
Just for the sake of general interest, I found an article where the author suggests the opposite strategy (he suggests to use ThreadPool instead of Thread)
geekswithblogs.net/.../...dPool-in-WebService.aspx
The context is different (infact the author's article talks about the ThreadPool applied to WebServices), so I'm not sure that a comparision with your article would correctly apply.

foobar
foobar United States
6/19/2007 1:57:20 PM #

Using a separate thread for processing only helps if that process is not CPU-bound on the machine that the initial thread runs on.

Anyhoo, the Page object has RegisterAsyncTask and ExecuteRegisteredAsyncTasks methods that can effectively run synchronous methods asynchronously.  There's an MSDN article out there that's a decent tutorial.

matt
matt United States
6/19/2007 6:29:09 PM #

Can't the ThreadPool's size be configured?

It also seems like poor design to me that ASP.NET would "steal" threads from "your" ThreadPool. Ideally the ThreadPool would be left for the end developer/application to utilize, not an intermediate framework.

Claus
Claus Denmark
6/20/2007 6:24:50 AM #

matt, you can reconfig it.

support.microsoft.com/default.aspx

Claes
Claes Sweden
6/21/2007 7:10:39 AM #

Just don't forget that the reason for a threadPOOL in the first place is that the threads are precreated and cheap to get a hold on.
Creating threads is an expensive operation and exxesive(?) thread swithing between all newly created threads also steal performance from the application.

Rob Williams
Rob Williams United States
12/16/2008 11:08:33 PM #

Using Thread.Start() risks unconstrained thread growth. If you are creating a new thread every time your page is requested and your page is being hammered with 1000 requests a second, then you can easily find yourself creating so many new threads that the increased CPU contention actually decreases rather than increases scalability, so I don't recommend using Thread.Start() in ASP.Net. You could use a custom thread pool, but personally I recommend using the Begin and End Methods provided within the Framework by most I/O operations (web requests, web service calls, BeginExecuteReader, etc.) and calling them with RegisterAsyncTask. That way, the Thread pool is used as efficiently as possible. I also recommend increasing the size of the threadpool using the article referenced by Claus above. For anyone who is interested, multithreading options in ASP.Net are explored quite thoroughly here: asyncassassin.com/.../...-Threading-in-ASPNET.aspx and here: asyncassassin.com/.../...-Threading-in-ASPNET.aspx

Pingbacks and trackbacks (2)+

Comments are closed

About the author

Mads Kristensen

Mads Kristensen
Program Manager at the Microsoft Web Platform team and founder of BlogEngine.NET.

More...

Month List

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer’s view in any way.