What is multithreading?

Multithreading is a widespread programming and execution model that allows multiple threads to exist within the context of a single process. They share the process’s resources, but they are able to execute independently. Single threading, on the other hand, involves the processing of one command at a time.

The purpose of threading is to enable a computer to do more than one thing at a time. In a single-core computer, multithreading won’t offer much of an advantage in terms of overall speed. However, in computers with multiple processor cores, which are common these days, multithreading can take advantage of the additional cores to perform separate instructions simultaneously, or by splitting the tasks between the cores.

Multithreading programming in .NET

There are few ways to do multithreading programming in .NET, for example:

  • Using Thread class
  • Using ThreadPool class
  • Using Task class
  • Using BackgroundWorker class

Code snippets and benchmark results

All the following code snippets have been tested on my PC with the following specs: Intel(R) Core(TM) i3-4130 CPU @ 3.40GHz (2 cores, 4 threads).

Thread class

The Thread class is used for creating and manipulating a thread in Windows.

Thread[] t = new Thread[threadCount];
for (int i = 0; i < threadCount; i++)
{
    t[i] = new Thread(() =>
    {
        DoComplexWork();
    });
    t[i].Priority = ThreadPriority.Highest;
    t[i].Start();
}
foreach (var ct in t)
{
    ct.Join();
}

ThreadPool class

The ThreadPool class manages a group of threads in which tasks are added to a queue and automatically started when threads are created.

using (CountdownEvent signaler = new CountdownEvent(threadCount))
{
    for (int i = 0; i < threadCount; i++)
    {
        ThreadPool.QueueUserWorkItem((x) =>
        {
            DoComplexWork();
            signaler.Signal();
        });
    }
    signaler.Wait();
}

Task class

A Task represents asynchronous operation and is part of the Task Parallel Library, a set of APIs for running tasks asynchronously and in parallel.

Task[] taskList = new Task[threadCount];
for (int i = 0; i < threadCount; i++)
{
    taskList[i] = new Task(new Action(() =>
    {
        DoComplexWork();
    }));
    taskList[i].Start();
}
Task.WaitAll(taskList);

BackgroundWorker class

The BackgroundWorker class executes an operation on a separate thread.

BackgroundWorker[] backgroundWorkerList = new BackgroundWorker[threadCount];
using (CountdownEvent signaler = new CountdownEvent(threadCount))
{
    for (int i = 0; i < threadCount; i++)
    {
        backgroundWorkerList[i] = new BackgroundWorker();
        backgroundWorkerList[i].DoWork += delegate (object sender, DoWorkEventArgs e)
        {
            DoComplexWork();
            signaler.Signal();
        };
        backgroundWorkerList[i].RunWorkerAsync();
    }
    signaler.Wait();
}

Benchmark results

Single threading8419ms
Multithreading using Thread7532ms
Multithreading using ThreadPool2901ms
Multithreading using Task3061ms
Multithreading using BackgroundWorker3100ms

To summarize, ThreadPool, Task, and BackgroundWorker can perform up to 2 times faster compared to Thread and the single threading method. Therefore, if you are going to perform any CPU-intensive task, you can always take advantage of multithreading programming in your code to improve the performance of your application. I have found that the BackgroundWorker class is easy to use and is very popular among developers I have worked with.