Tasks are a way to manage asynchronous task execution in .NET. They allow performing background operations without blocking the main thread, resulting in a significant improvement in user experience and program efficiency.
Tasks are useful, for example
- Input/Output (I/O) operations: such as reading or writing to a file, database access, or making HTTP requests.
- Intensive processing: operations that consume a lot of CPU and could slow down the user interface in desktop or mobile applications.
- Parallel execution: when multiple tasks need to be executed at the same time and wait for their results.
What are Tasks in C#?
A Task is an object that represents a task that is currently running or pending execution. This task can be composed of several operations and can be executed in parallel with other Tasks.
In C#, a Task is a class (System.Threading.Tasks.Task
), and it has a state that indicates its progress; once it is complete, its result can be retrieved.
Basic Creation and Use of Tasks
A Task can be created in several ways in C#. The most common ways are:
- Using the
Task
class directly - Utilizing
async
andawait
Creating a Task
To create a basic Task, you can use the Task
class and start a new task with Task.Run()
or Task.Factory.StartNew()
.
For example, if you want to create a Task to perform a mathematical operation, you can write the following:
Task t = Task.Run(() => {
int result = 1 + 2;
Task.Delay(2000).Wait(); // Waits for 2 seconds
Console.WriteLine("Result: " + result);
});
Here,
Task.Run
: Creates and executes a Task that will run asynchronously.Task.Delay
: Simulates a wait task (in this case, 2 seconds) before finishing the Task execution.
Waiting for a Task to Complete
Once a Task has been created, it may be necessary to wait for it to complete before continuing with other operations.
To do this, you can use the Task’s Wait()
method, which blocks the current thread until the Task completes.
Task t = Task.Run(() => {
int result = 1 + 2;
Console.WriteLine("Result: " + result);
});
t.Wait();
Using async and await
To simplify the handling of asynchronous Tasks, C# introduces the keywords async
and await
. The async
modifier is placed in the declaration of a method, and await
allows waiting for a Task to finish without blocking the main thread. Here is a basic example:
public static async Task Main()
{
Console.WriteLine("Starting the asynchronous operation...");
await AsynchronousOperation();
Console.WriteLine("Operation completed.");
}
public static async Task AsynchronousOperation()
{
await Task.Delay(2000); // Asynchronous wait of 2 seconds
Console.WriteLine("Inside the asynchronous operation.");
}
We see it in more detail in the following post async/await
Exception Handling in Tasks
Exception handling can become somewhat complicated. Exceptions in Tasks do not propagate immediately to the main thread; instead, they remain stored in the Task and must be caught using try-catch
blocks along with await
or task.Wait()
.
using System;
using System.Threading.Tasks;
public class Program
{
public static async Task Main()
{
try
{
await Task.Run(() => { throw new InvalidOperationException("Error in the Task"); });
}
catch (Exception ex)
{
Console.WriteLine($"Captured Exception: {ex.Message}");
}
}
}
In this example, the exception thrown inside the Task is captured by the try-catch
block that wraps the await
.
Executing Multiple Tasks: WhenAll and WhenAny
C# provides two very useful methods for working with multiple Tasks at the same time: Task.WhenAll
and Task.WhenAny
.
Task.WhenAll
This method allows executing several Tasks concurrently and waits until all of them complete.
using System;
using System.Threading.Tasks;
public class Program
{
public static async Task Main()
{
Task task1 = Task.Delay(2000);
Task task2 = Task.Delay(3000);
await Task.WhenAll(task1, task2); // Waits for both tasks to finish
Console.WriteLine("Both tasks completed.");
}
}
Task.WhenAny
Task.WhenAny
allows continuing execution as soon as any of the Tasks has finished. This method is useful when you need the quickest response among several operations.
using System;
using System.Threading.Tasks;
public class Program
{
public static async Task Main()
{
Task task1 = Task.Delay(2000);
Task task2 = Task.Delay(3000);
await Task.WhenAny(task1, task2); // Continues when one task finishes
Console.WriteLine("At least one task completed.");
}
}