ThreadPool manages a group of threads. We process jobs in parallel using thread pools. With the ThreadPool class, we constrain threads and incrementally update a ProgressBar. We can use a multiple-core architecture for batch processing.

Intro. The .NET Framework provides us with the System.Threading namespace, which includes the ThreadPool class. This is a static class that you can directly access. It provides us with the essential parts of thread pools.

Tip: It is useful for running many separate tasks in the background. There are better options for a single background thread.

Maximum number of threads. This is usually entirely useless to know. The whole point of ThreadPool in .NET is that it internally manages the threads in the ThreadPool. Multiple-core machines will have more threads than older machines.

Thread pools typically have a maximum number of threads. If all the threads are busy, additional tasks are placed in queue until they can be serviced as threads become available. MSDN

Usage. The ThreadPool type can be used on servers and in batch processing applications. ThreadPool has internal logic that makes getting a thread much less expensive. Threads are already made and are just "hooked up" when required.

Thread pools are often employed in server applications. Each incoming request is assigned to a thread from the thread pool, so the request can be processed asynchronously, without tying up the primary thread or delaying the processing of subsequent requests.

Threading is important but for most apps that don't take a long time to execute and are only doing one thing, it is not important. For applications whose interface usability isn't important, avoid threads as well.

BackgroundWorker. If you are using Windows Forms, prefer the BackgroundWorker for simpler threading requirements. BackgroundWorker does well with network accesses and other simple stuff. For batch processing with many processors, you need ThreadPool.

The program does batch processing. Consider ThreadPool. This better automates multiple short-lived threads.

It makes three or more threads. Also consider ThreadPool, which handles multiple threads, not just one.

It uses Windows Forms. Consider BackgroundWorker, an easy-to-use control that you can add in the Visual Studio designer.

You have many short-lived threads. Use ThreadPool—the alternative is to implement your own ThreadPool with Thread objects. This is harder.

Methods. You can hook up methods to the ThreadPool by using QueueUserWorkItem. You have your method you want to run on the threads. And you must hook it up to QueueUserWorkItem. How can you do this?

We must use WaitCallback. At MSDN, WaitCallback is described as a delegate callback method to be called when the ThreadPool executes. It is a delegate that "calls back" its argument.

WaitCallback. You can use WaitCallback by simply specifying the "new WaitCallback" syntax as the first argument to ThreadPool.QueueUserWorkItem. You don't need any other code to make this approach effective.

Example that uses WaitCallback: C#
void Example()
{
// Hook up the ProcessFile method to the ThreadPool.
// Note: 'a' is an argument name. Read more on arguments.
ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessFile), a);
}
private void ProcessFile(object a)
{
// I was hooked up to the ThreadPool by WaitCallback.
}

Parameters. You can use parameters by defining a special class and putting your important values inside of it. Then, the object is received by your method, and you can cast it. Here's an example that builds on the earlier ones.

In this example, we are sending two values to the ProcessFile threaded method. It needs to know the FileName and the SelectedIndex. We send all this information in the object parameter.

ProgressBar. You can use the ProgressBar by adding the Windows Forms control in the Toolbox panel on the right to your Windows program in the designer. Next, you have to deal with progressBar1.Value, progressBar1.Minimum and progressBar1.Maximum.

Note: The Value is your position between the minimum and the maximum. Initialize your ProgressBar like this.

Example that sets ProgressBar: C#// Set progress bar length.
// Here we have 6 units to complete, so that's the maximum.
// Minimum usually starts at zero.
progressBar1.Maximum = 6; // or any number
progressBar1.Minimum = 0;

ProgressBar position. The length of the colored part of your ProgressBar is the Value's percentage of the Maximum. So, if the Maximum is 6, a Value of 3 will display as halfway done.

Invoke, ProgressBar. Let's look at how to use the Invoke method on the ProgressBar instance. Unfortunately, you can't access Windows controls on worker threads, as the UI thread is separate. We have to use a delegate and Invoke onto the ProgressBar.

So: We set the Maximum and Minimum on the ProgressBar. We invoke the delegate method after the work is completed to increment the size.

Debugger. Once you have a program working, you can take these steps to visualize the threads. First, open your threaded app in debug mode. Once your application is running in the debugger, tell it to do its job and run the threads.

So: Run the debugger with the green arrow and when the threads are running, hit the 'pause' button in the toolbar.

Next steps. Please go to Debug > Windows > Threads. This menu item will open a window that looks like the one here. You can see exactly how many threads are running in the ThreadPool.

Note: The above image shows ten threads total, but four of the worker threads are assigned to MainWindow.ProcessFile.

Counts. If you have a dual-core or quad-core system, you will want at most two or four demanding threads. We can do this by keeping a _threadCount field and tracking the number of running threads.

With this thread count field, you will need to use a lock in the C# language to avoid having the field incorrectly read or written. Locks shield your thread from changes in other threads.