Tasks in C# provide a powerful and flexible way to handle asynchronous programming and parallelism. They represent asynchronous operations and provide methods for managing and controlling these operations. This tutorial will cover the basics of tasks, how to create and manage them, common patterns, and best practices.
A task represents an asynchronous operation. It can be used to perform background work, execute operations concurrently, and improve the responsiveness of applications. Tasks are managed by the Task Parallel Library (TPL), which provides a high-level abstraction for parallel programming.
You can create and start tasks using the Task class. Here's a simple example:
using System;
using System.Threading.Tasks;
namespace TaskExamples;
class Program
{
static void Main(string[] args)
{
Task task = new Task(DoWork);
task.Start();
task.Wait(); // Wait for the task to complete
Console.WriteLine("Main thread continues to run...");
}
static void DoWork()
{
Console.WriteLine("Task is running...");
}
}
Tasks can return values using the Task<TResult> type. This allows you to perform asynchronous operations that produce results. Here's an example:
using System;
using System.Threading.Tasks;
namespace AsyncExamples;
class Program
{
static async Task Main(string[] args)
{
int result = await CalculateSumAsync(5, 10);
Console.WriteLine($"Sum: {result}");
}
static async Task<int> CalculateSumAsync(int a, int b)
{
await Task.Delay(1000); // Simulate asynchronous work
return a + b;
}
}
You can wait for tasks to complete using the Wait method or the await keyword. Here's an example of waiting for a task to complete:
using System;
using System.Threading.Tasks;
namespace TaskExamples;
class Program
{
static async Task Main(string[] args)
{
Task task = DoWorkAsync();
await task; // Wait for the task to complete
Console.WriteLine("Main thread continues to run...");
}
static async Task DoWorkAsync()
{
await Task.Delay(1000); // Simulate asynchronous work
Console.WriteLine("Task is running...");
}
}
Task continuations allow you to specify actions that should run after a task completes. This can be done using the ContinueWith method. Here's an example:
using System;
using System.Threading.Tasks;
namespace TaskExamples;
class Program
{
static void Main(string[] args)
{
Task task = Task.Run(() => DoWork())
.ContinueWith(t => Console.WriteLine("Continuation task is running..."));
task.Wait(); // Wait for the continuation task to complete
Console.WriteLine("Main thread continues to run...");
}
static void DoWork()
{
Console.WriteLine("Task is running...");
}
}
You can cancel tasks using a CancellationToken
. This allows you to request the cancellation of a task and handle the cancellation within the task. Here's an example:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace TaskExamples;
class Program
{
static async Task Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
Task task = DoWorkAsync(cts.Token);
cts.CancelAfter(500); // Cancel the task after 500 milliseconds
try
{
await task;
}
catch (OperationCanceledException)
{
Console.WriteLine("Task was cancelled.");
}
}
static async Task DoWorkAsync(CancellationToken token)
{
for (int i = 0; i < 10; i++)
{
token.ThrowIfCancellationRequested();
await Task.Delay(200); // Simulate asynchronous work
Console.WriteLine("Task is running...");
}
}
}
Exceptions in tasks can be handled using try-catch blocks. When an awaited task throws an exception, it is captured and can be re-thrown in the calling method. Here's an example:
using System;
using System.Threading.Tasks;
namespace TaskExamples;
class Program
{
static async Task Main(string[] args)
{
try
{
await DoWorkAsync();
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex.Message}");
}
}
static async Task DoWorkAsync()
{
await Task.Delay(1000); // Simulate asynchronous work
throw new InvalidOperationException("Something went wrong.");
}
}
The Task Parallel Library (TPL) provides methods for running tasks in parallel, such as Parallel.For and Parallel.ForEach. These methods can significantly improve performance for certain types of operations. Here's an example:
using System;
using System.Threading.Tasks;
namespace TaskExamples;
class Program
{
static void Main(string[] args)
{
Parallel.For(0, 10, i =>
{
Console.WriteLine($"Task {i} is running...");
});
}
}
Following best practices when using tasks can help you write efficient and maintainable asynchronous code. Here are some best practices to consider:
Tasks provide a powerful and flexible way to handle asynchronous programming and parallelism in C#. By understanding how to create, manage, and control tasks, and following best practices, you can build efficient and responsive applications.