Introduction
The .NET Framework BCL contains a very nice implementation of a thread pool (the System.Threading.ThreadPool
class).
But, this class is not suitable for the following scenarios:
- Long-running operations. Usually, for long-running operations, it is recommended to use the
Thread
class. ThreadPool
is per process. It means that a situation when there is no available thread in the ThreadPool
can happen pretty often. What if you have a very important and emergent work item and do not want to take such a risk? But pretty often, especially when you have an application with a number of app domains (like IIS or SQL server), you can run out of threads in the thread pool... ThreadPool
does not support IAsyncResult
. BeginInvoke
methods of all delegates internally pass control to ThreadPool
, but ThreadPool
itself does not support IAsyncResult
.
Generally, there are number of strict recommendations about when you should use ThreadPool
and when you should use the Thread
class directly or MulticastDelegate.BeginInvoke
. The main problem of System.Threading.ThreadPool
is that it is per process. So, if you have a set of very important tasks to do, and also have a set of third-party assemblies that you host in the application, there is always a probability that your important tasks will be delayed. In the case of the CustomThreadPool
, you have a separate threadpool for each application domain.
You can have as many CustomThreadPool
s as application domains.
This is just the initial version of CustomThreadPool
, and I plan to extend it in future. Maybe, instead of WaitHandle
s, I will use Monitor.Wait
and Monitor.Pulse
to achieve better flexibility (and probably performance).
Using the Code
The CustomThreadPool
has three methods: QueueUserWorkItem
, QueueUserWorkItemResult<tresult>
, and RetreiveUserWorkItemResult
.
A sample of how to use these methods is provided below:
public static class Program
{
public static void Main()
{
CustomThreadPool.QueueUserWorkItem(DoSomething);
CustomThreadPool.QueueUserWorkItemResultt<int>(MyLongRunningFunc,
MyLongRunningFuncFinished);
}
private static void DoSomething()
{
Console.WriteLine("Hello, world!");
}
private static int MyLongRunningFunc()
{
return 888;
}
private static void MyLongRunningFuncFinished(IAsyncResult result)
{
Console.WriteLine(CustomThreadPool.RetreiveUserWorkItemResult<int>(result));
}
}
Points of Interest
I've learned a lot about multithreading, especially about volatile fields and IAsyncResult
implementation.
History
- 20 Nov, 2008 -- Initial Posting
- 10 Mar, 2009 -- Added dispose functionality to
CustomThreadPool
. Class changed from static to "instanceable" to support creation of multiple instances within the same App Domain.