Task Concepts
In .NET, a Task represents an asynchronous operation. Tasks are fundamental to building responsive and scalable applications, especially in scenarios involving I/O-bound operations like network requests or file system access.
What is a Task?
A Task is an object that represents an ongoing operation. It can be used to track the progress of an asynchronous operation, wait for its completion, and retrieve its result or exception. Tasks are managed by the Task Parallel Library (TPL).
Tasks are a more flexible and powerful abstraction than older asynchronous patterns like the `Begin`/`End` methods. They integrate seamlessly with C#'s async and await keywords, making asynchronous code look and behave more like synchronous code.
Creating and Running Tasks
You can create a task in several ways. One common method is using the Task.Run method, which executes a delegate asynchronously on a thread pool thread.
using System;
using System.Threading.Tasks;
public class TaskExample
{
public static async Task Main(string[] args)
{
Console.WriteLine("Starting an asynchronous operation...");
// Create and run a task
Task myTask = Task.Run(() =>
{
// Simulate a long-running operation
System.Threading.Thread.Sleep(2000);
Console.WriteLine("Asynchronous operation completed.");
});
Console.WriteLine("Main thread continues executing...");
// Wait for the task to complete
await myTask;
Console.WriteLine("All operations finished.");
}
}
For tasks that return a value, use Task<TResult>.
using System;
using System.Threading.Tasks;
public class TaskResultExample
{
public static async Task Main(string[] args)
{
Console.WriteLine("Starting a task that returns a value...");
Task<int> calculationTask = Task.Run(() =>
{
// Simulate a calculation
System.Threading.Thread.Sleep(1500);
return 42 * 2;
});
Console.WriteLine("Main thread is free to do other work...");
// Get the result when the task is complete
int result = await calculationTask;
Console.WriteLine($"The result is: {result}");
}
}
The `async` and `await` Keywords
The async and await keywords are syntactic sugar that simplifies working with tasks.
async: This modifier is applied to a method signature to indicate that the method contains one or moreawaitexpressions. Anasyncmethod can execute synchronously or asynchronously.await: This operator is used within anasyncmethod to suspend the execution of the method until the awaited task completes. While waiting, the thread is released to perform other work.
async and await is the recommended way to handle asynchronous operations in modern .NET development.
Task Status
A task has a status that indicates its current state. Common statuses include:
Created: The task has been created but has not yet been scheduled.WaitingToRun: The task has been scheduled but has not yet started execution.Running: The task is currently executing.WaitingForChildrenToComplete: The task is waiting for one or more child tasks to complete.RanToCompletion: The task completed successfully.Canceled: The task was canceled.Faulted: The task completed with an exception.
You can check the status of a task using the Status property.
Task Completion Sources
TaskCompletionSource<TResult> is useful when you need to manually signal the completion of a task. This is often seen when bridging older asynchronous patterns with the TPL or when creating custom asynchronous operations.
using System;
using System.Threading.Tasks;
public class TaskCompletionSourceExample
{
public static Task<string> GetDataAsync(int id)
{
var tcs = new TaskCompletionSource<string>();
// Simulate an asynchronous operation that might call back
// In a real scenario, this might be a callback from a library
// or a UI event handler.
Task.Run(() =>
{
try
{
// Simulate fetching data
System.Threading.Thread.Sleep(1000);
if (id == 0)
{
throw new ArgumentException("ID cannot be zero.");
}
tcs.SetResult($"Data for ID {id}");
}
catch (Exception ex)
{
tcs.SetException(ex);
}
});
return tcs.Task;
}
public static async Task Main(string[] args)
{
try
{
string data = await GetDataAsync(10);
Console.WriteLine($"Received: {data}");
// This will throw an exception
// string dataError = await GetDataAsync(0);
// Console.WriteLine($"Received: {dataError}");
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
}
Task Cancellation
Tasks can be canceled using a CancellationTokenSource and a CancellationToken. This is crucial for preventing resource leaks and allowing users to abort long-running operations.
using System;
using System.Threading;
using System.Threading.Tasks;
public class TaskCancellationExample
{
public static async Task PerformWorkWithCancellationAsync(CancellationToken cancellationToken)
{
Console.WriteLine("Work started...");
for (int i = 0; i < 10; i++)
{
cancellationToken.ThrowIfCancellationRequested(); // Check for cancellation
Console.WriteLine($"Doing work step {i}...");
await Task.Delay(500); // Simulate work, allowing cancellation
}
Console.WriteLine("Work completed successfully.");
}
public static async Task Main(string[] args)
{
var cts = new CancellationTokenSource();
var cancellationToken = cts.Token;
Console.WriteLine("Starting task that can be cancelled. Press Enter to cancel.");
var workTask = PerformWorkWithCancellationAsync(cancellationToken);
// If Enter is pressed, cancel the task
if (Console.ReadLine() == "")
{
Console.WriteLine("Cancelling task...");
cts.Cancel();
}
try
{
await workTask;
Console.WriteLine("Task finished normally.");
}
catch (OperationCanceledException)
{
Console.WriteLine("Task was cancelled.");
}
catch (Exception ex)
{
Console.WriteLine($"An unexpected error occurred: {ex.Message}");
}
}
}