Multithreading programming in .NET

What is multithreading?

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

The purpose of threading is to allow computer to do more than one thing at a time. In a single core computer, multithreading won’t give much advantages for overall speed. But for computer with multiple processor cores (which is so common these days), multithreading can take advantage of additional cores to perform separate instructions at the same time 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.

Stopwatch sw = Stopwatch.StartNew();
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();
}
sw.Stop();
Console.WriteLine("Elapsed time: {0}ms", sw.ElapsedMilliseconds);

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.

Stopwatch sw = Stopwatch.StartNew();
using (CountdownEvent signaler = new CountdownEvent(threadCount))
{
    for (int i = 0; i < threadCount; i++)
    {
        ThreadPool.QueueUserWorkItem((x) =>
        {
            DoComplexWork();
            signaler.Signal();
        });
    }
    signaler.Wait();
}
sw.Stop();
Console.WriteLine("Elapsed time: {0}ms", sw.ElapsedMilliseconds);

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.

Stopwatch sw = Stopwatch.StartNew();
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);
sw.Stop();
Console.WriteLine("Elapsed time: {0}ms", sw.ElapsedMilliseconds);

BackgroundWorker class

The BackgroundWorker class executes an operation on a separate thread.

Stopwatch sw = Stopwatch.StartNew();
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();
}
sw.Stop();
Console.WriteLine("Elapsed time: {0}ms", sw.ElapsedMilliseconds);

Benchmark results

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

To summarize, ThreadPool, Task and BackgroundWorker can perform 2x faster compared to Thread and single threading method. So, if you’re going to perform any CPU intensive task, you can always take advantage of multithreading programming in your code to have better performance in your application. I could say that BackgroundWorker class is easy to use and very popular among developers I have been working with.