C# - What is the difference between threads and tasks?

I had a discussion a couple of weeks ago about tasks and threads. It occurred to me that I had rarely (never?) used threads and only ever used tasks. So I decided to write this post on threads and tasks. Let us jump right to 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 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 asynchronous. 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 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 pool of threads, which are ready to carry out instructions (if they are no occupied of course). You can see a Task as a higher level abstraction upon 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 one is using a thread:

int result = 0;
Thread thread = new System.Threading.Thread(() => { 
    result = 1; 
});
thread.Start();
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, I cannot remember if I have ever used a thread for something else 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 is an easy way to "wait" for the task to finish and resume when it is completed.

Summary

Here is a quick summary of the above:

  • Tasks are a higher level concept: tasks are 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 canceled. This token can be passed along to other tasks which will be canceled as well.E
  • Tasks support async/await: async/await is a simple way to wait for 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!