Working with threads in .NET is fairly straightforward. You have two options:
- Manually creating new threads (usually for longer running tasks) and
- Using .NET’s built-in thread pool (typically for shorter running tasks)
Option 1 - Manually create your own thread (Works for both short-running and long running tasks)
Say you have a method (a long running task) that you want to assign to a new thread. All you need to do is pass in the name of the long running method to ThreadStart. ThreadStart is a built-in .NET class which contains all the necessary plumbing for creating a thread, passing values to the method on the thread, getting notifications back from it etc. The example below is the simplest possible usage of ThreadStart.
using System.Threading;
using System.Diagnostics;
namespace ManualThreadCreation
{
class Program
{
static void Main(string[] args)
{
ThreadStart ts = new ThreadStart(LongRunningTask);
Thread t = new Thread(ts);
t.Start();
}
static void LongRunningTask()
{
// ... See Zipped Code in the downloads below
}
}
}
Option 2 - Use .NET’s built-in thread pool (well suited if all your tasks are short running)
Say you have more than one task that needs to get done – and each task is fairly short in duration. As an example, consider a given geometric shape for which you need to
- a) calculate its area (Task 1) and
- b) Calculate its volume (Task 2).
Say that you do not want to deal with creating a new thread manually (option 1) – because then you have to worry about correctly disposing of the thread when you are done, you have to worry about the thread inadvertently accessing resources that another thread may be using (concurrency) etc. Since you are a ‘keep it simple’ kind of person, you want to let .NET handle all these threading details for you. All you want to do is to be able to say – Give me a thread – and execute task1 on it, Give me another thread and execute task2 on it.
Again, it is important to emphasize that this approach (using the Thread Pool) be used primarily for shorter running tasks. If even one of our tasks was longer running – we would be better off with manually creating threads (Option 1). For example – say your workflow requires a real time background check on a loan applicant. You decide that you can use one thread to perform a ‘personal credit’ check while you use another thread to do a ‘criminal background’ check’. Since each of these tasks could be fairly time consuming, using .NET’s thread pool would not be a good way to go. You would need to resort to method 1 – which involves manually creating the individual threads for each of the long running tasks.
The main components of using .NET’s ThreadPool are shown below (full zipped code available for download below). As one can see, using a ‘ready-to-use’ thread from the thread pool just involves a single line of code – ThreadPool.QueueUserWorkItem. The supporting objects (state and EventWaitHandle) are explained in the full code (see zipped solution download below)
State state1 = new State(new Sphere(radius));
// Task1 - Calculate the area() of the shape (passed in inside the state object)
ThreadPool.QueueUserWorkItem(new WaitCallback(CalculateArea), state1);
// Task2 - Calculate the Volume() of the shape
State state2 = new State(new Sphere(radius));
ThreadPool.QueueUserWorkItem(new WaitCallback(CalculateVolume), state2);
// WaitOne means - no one else is allowed to use this thread till its task (CalculateArea) is done
state1.eventWaitHandle.WaitOne();
// WaitOne means - no one else is allowed to use this thread till its task (CalculateVolume) is done
state2.eventWaitHandle.WaitOne();
Option 3 - Multithreading with just ONE thread (for REALLY short tasks)
Creating new threads has an overhead associated with it. Often times, when we have really short tasks, the whole purpose of using multiple threads to execute them is defeated because the time taken to create a new thread would be longer than the time to actually execute the task. So – how do we still “multithread” without creating new threads? The answer is ‘ simply use the same thread to execute multiple tasks. More on this in a future blog.
Summary
Suppose you are faced with executing multiple tasks in parallel. The first question you need to address is ‘Are any of these tasks long-running ?’ If yes, then your best bet is to manually create threads – and avoid .NET’s thread pool altogether. If all your tasks are short-running, then you can either use the ThreadPool or better still – just use a single thread to execute them serially.The full source code for Option 1 (Manual Thread Creation) and Option 2 (Using .NET’s thread pool) in C# is provided below.