C# - tasks vs threads, what is the difference between threads and tasks? - updated 2023

I had a discussion a couple of weeks ago about tasks and threads. It occurred to me that I had rarely used threads and mostly used tasks in my career. So I decided to write this post on threads and tasks to be more clear on the differences, let us jump right into it and define the two.

Defining threads and tasks

MSDN defines a Task as the following:

"a single operation that does not return a value and that usually executes asynchronously" (You can use Task<Result> to return a result).

Threads on the other hand, are not a .NET construct, they are built into your operating system. The thread class from .NET is just a way to create and manage threads. Wikipedia defines a thread as the following

"thread of execution is the smallest sequence of programmed instructions that can be managed independently by a scheduler"

So how are they different?

All in all a Task uses a thread in order to execute asynchronously. A Task can be seen as a convenient and easy way to execute something asynchronously and in parallel. In other programming languages and frameworks this may be known as a promise - "I promise I will return to you at some point". A task will by default use the Threadpool, which saves resources as creating threads can be expensive. A threadpool is.. a reusable pool of threads, which are ready to carry out instructions - if they are not occupied of course. You can see a Task as a higher level of abstraction of threads, which could be a reason why they are under the System.Threading namespace.

You can accomplish the same with a thread as you can with a task. Take a look at the following examples, the first example is using a thread:

int result = 0;
Thread thread = new System.Threading.Thread(() => { 
    result = 1; 
thread.Join(); //Blocks the calling thread until the thread terminates (is done) 
Console.WriteLine(result); //is 1

The second example is using a task:

int result = await Task.Run(() => {
    return 1; 
Console.WriteLine(result); //is 1

In the above the use of Task is a lot simpler and has no thread jargon. You create a new task and wait synchronously for the result using await. Normally a Task is all you need, think about it when was the last time you had to use a thread for something other than experimentation?

async and await

Tasks also support the async / await keywords, Microsoft docs describes the await keyword as:

The await operator suspends evaluation of the enclosing async method until the asynchronous operation represented by its operand completes. When the asynchronous operation completes, the await operator returns the result of the operation, if any.

It is important to note that this does not block the thread but returns the control to the awaiting caller. In simple terms this means that it is an easy way to "wait" for the task to finish and then resume when it is completed.


Here is a quick summary of the above:

  • Tasks are a higher level concept: A task is basically a promise to run a function and return when it is done.
  • Threads are a lower level concept: Threads are a part of your operating system and the thread class is a way to manage them.
  • Leveraging the thread pool: tasks use the thread pool, which is a "pool" of threads that can be used and reused. Creating threads can be expensive, which is why we have the thread pool.
  • Threads do not naturally return anything: Tasks are able to return an object when they are completed, which makes them great for executing a method and returning the result asynchronously.
  • Cancellation tokens: Tasks can use cancellation tokens so that they can be requested to be cancelled. This token can be passed along to other tasks which will be cancelled as well.
  • Tasks support async/await: async/await is a simple way to wait for an asynchronous method to finish without blocking the thread.

That is it!

I hope you liked this post, let me know what you think in the comments! Did I miss something, write it in the comments please.