Don’t use the ThreadPool in ASP.NET

by Mads Kristensen 17. June 2007 22:54

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.

* Only $4.95/month ASP.NET & Windows 2008 + IIS 7 Hosting! FREE SQL Included

Tags:

ASP.NET

Comments

6/18/2007 4:42:31 AM #

Michael Nemtsev

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

Michael Nemtsev Russia |

6/18/2007 4:59:09 AM #

Claus

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.

Claus Denmark |

6/18/2007 2:56:55 PM #

Michael Nemtsev

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

Michael Nemtsev Russia |

6/18/2007 9:55:07 PM #

mcgurk

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.

mcgurk United States |

6/18/2007 10:07:35 PM #

Mads Kristensen

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

Mads Kristensen Denmark |

6/19/2007 7:13:32 AM #

NinjaCross

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.

NinjaCross Italy |

6/19/2007 10:57:20 PM #

foobar

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.

foobar United States |

6/20/2007 3:29:09 AM #

matt

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.

matt United States |

6/20/2007 3:24:50 PM #

Claus

matt, you can reconfig it.

support.microsoft.com/default.aspx

Claus Denmark |

6/21/2007 4:10:39 PM #

Claes

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.

Claes Sweden |

7/11/2007 1:21:48 AM #

pingback

Pingback from drowningintechnicaldebt.com

re: Testing your code - roy ashbrook

drowningintechnicaldebt.com |

12/17/2008 8:08:33 AM #

Rob Williams

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

Rob Williams United States |

11/19/2009 4:45:04 PM #

pingback

Pingback from jonathangeorge.wordpress.com

Make methods “fire and forget” with PostSharp « Jonathan George's Blog

jonathangeorge.wordpress.com |

Comments are closed

About the slave

Mads Kristensen Mads Kristensen
Web developer at ZYB and founder of BlogEngine.NET. More...

LinkedIn ZYB Facebook Last.fm Twitter View Mads Kristensen's profile on Technorati

The Lounge

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2008