C#: The ThreadPool: A Powerful Little Multitasking Technique

The ThreadPool construct is a common way to create a multitasking program. Some of the most complex applications can be built using a single Windows Form plus a set of ThreadPool threads. The Form will be the basis of all user interactions, with various buttons, links and lists that trigger other ThreadPool threads to be launched. Then, each independent thread could handle various tasks that will essentially run in parallel with all the other threads. Below is a flowchart of a trivial example using a ThreadPool to manage 2 independent threads running “simultaneously”:

Flowchart

Some quick explanation about the above flowchart:

  • The main worker task (or thread) will initiate two new tasks, each using a thread from the C# ThreadPool
  • After launching the 2 separate tasks, the main thread will enter a 1/10 second loop checking if all threads are done
  • The first ThreadPool thread will fetch Google’s home page to find the length of the HTML for that page
  • The second ThreadPool thread will check every odd integer to see if it’s a prime until it finds the 1,000,000th prime number
  • To be able to determine if all ThreadPool threads are done, a count of active threads tracks threads launched and completed

Code Sample

Below is the sample of code showing a complete program demonstrating the use of a ThreadPool as depicted in the above flowchart:

using System;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;

namespace MultitaskingTimers
{
	internal class Program
	{
		static int nActiveThreadCount = 0;

		static void Main(string[] args)
		{
			ThreadPool.QueueUserWorkItem(GoogleThreadWorker);
			++nActiveThreadCount;
			ThreadPool.QueueUserWorkItem(PrimeThreadWorker);
			++nActiveThreadCount;

			// Loop waiting for all threads to exit OR a key was pressed

			while(nActiveThreadCount > 0)
			{
				if(Console.KeyAvailable) {
					break;
				}
				Thread.Sleep(100);
			}
			Console.WriteLine("All threads completed; exiting program.");
			Environment.Exit(0);
		}

		/// <summary>
		/// Get # of characters in the HTML of the Google Home Page
		/// </summary>
		/// <remarks> ### Worker Thread 1 ### </remarks>
		/// <param name="state">passed value</param>
		private static void GoogleThreadWorker(object state)
		{
			// Create a new WebRequest Object to the mentioned URL.
			// Set the 'Timeout' property in Milliseconds.
			// This request will throw a WebException if it times out before it is able to fetch the resource.
			int nLength = 0;        // length of Google home page HMTL
			try
			{
				WebRequest myWebRequest = WebRequest.Create("http://www.google.com");
				myWebRequest.Timeout = 10000;
				WebResponse myWebResponse = myWebRequest.GetResponse();
				Stream streamRx = myWebResponse.GetResponseStream();
				Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
				StreamReader readStream = new StreamReader(streamRx, encode);
				String strGoogleHome = readStream.ReadToEnd();
				nLength = strGoogleHome.Length;
				Console.WriteLine($"Thread 1 found Google home has {nLength} chars.");
			}
			catch(Exception ex)
			{
				Console.WriteLine($"HTML error: {ex.Message}");
			}
			--nActiveThreadCount;
		}

		/// <summary>
		/// Loop checking primes until 1,000,000th prime
		/// </summary>
		/// <remarks> ### Worker Thread 2 ### </remarks>
		/// <param name="state">passed value</param>
		private static void PrimeThreadWorker(object state)
		{
			int idx = 1;        // count of primes so far

			for(int i = 3; i < int.MaxValue; i += 2)
			{
				if(IsPrime(i))
				{
					if(++idx > 1000000)
					{
						Console.WriteLine($"{i} is the 1,000,000th prime");
						--nActiveThreadCount;
						break;
					}
				}
			}
		}
		private static bool IsPrime(int n)
		{
			for(int i = 3; i <= Math.Sqrt(n); i += 2)
			{
				if(n % i == 0) {
					return(false);
				}
			}
			return(true);
		}
	}
}

Your main focus for this program should be understanding the ThreadPool setup used in the Main method. Here you will see two ThreadPool threads being set up using the QueueUserWorkItem method, passing it the name of a method delegate using the “private static void xxx(object state)” format. Each thread uses a callback method being passed a nullable object that can contain any data that could be used by that thread callback instance. Since there is no simple way of determining whether all ThreadPool tasks are complete, I use a globally accesible integer variable to track the number of active tasks. After launching the two tasks, the main program simply loops checking each tenth of a second to see if the number of active tasks is 0. Each thread decrements the active thread count before it exits.

The two tasks are relatively simple. The first shows how to load a web page into a stream that can be read using the StreamReader object’s “ReadToEnd” method. This one loads Google’s home page and simply displays the number of bytes of HTML received. The second tasks locates the one millionth prime number and displays it.

Because this program is meant to be a console program (i.e. runs in a command prompt window), all the output is sent to that window. A typical output from the above program would be:

Multitasking continued…