What are the differences between Tasks and threads? What does Task offer that threads can not do? Basically, why do we need Tasks?
Threads are a mechanism for parallelization and Tasks are a mechanism for synchronization. In simpler terms, a thread is a worker, a task is a unit of work. Often the same worker can execute multiple units of work not in parallel though. It can achieve a great level of efficiency by prioritizing tasks and managing context which entirely done by .NET behind the scene.
Think of it this way. You, as a developer, arrive to work and you have two tickets to comlete. One ticket requires clarification from a project manager. So you send an email to the project manager asking for more information.
There is no really need to wait for a reply, you can start working on the second ticket. You are still a single thread (a single worker) but you are managing two tasks - two units of work. While you are working on the second ticket, the product manager responds with an answer. You can choose to switch context and return to executing Ticket 1 or you can wait till Ticket 2 is complete and only then switch the context back to ticket 1.
In an alternative scenario, you can pick a teammate and assign her Ticket 2. In this case, you are achieving genuine parallelization but you get two workers involved: yourself and your coworker.
In .NET spawning, a new thread is computationally very expensive. It is a good practice to use tasks whenever long-running operations are I/O bound such as reading from DB for example. It is better to use threads for CPU bound heavy operations such as heavy mathematical computations.
var productsTask = GetItemtsAsync(odredId) // dispatch call to DB
var userTask = GetUserInfoAsync(userId) // dispatch another call to DB
// wait until both calls are completed, their order is irrelevant
// and it is fully managed by the runtime
Task.AwaitAll(productsTask, userTask)
// now we have enough data to generate an invoice
// (this is all done on one thread)
var invoice = GenerateInvoice(productsTask.Result, userTask.Result)
By running DB calls in separate threads, you will make your code slower, since you still have no control over how long DB calls run and in addition, you will spend time on creating new threads (these would be very expensive operations, memory-wise and CPU cycle-wise)
Note: the code below accomplishes the same thing as the code snippet above but it is more elegant. Using .Result
is not advisable in certain scenarios
var productsTask = GetItemtsAsync(odredId) // dispatch call to DB
var userTask = GetUserInfoAsync(userId) // dispatch another call to DB
var invoice = GenerateInvoice(await productsTask, await userTask)