In C#, a Task
represents the result of an asynchronous operation. It allows you to perform work asynchronously and concurrently, without having to worry about thread management.
Creating a Task
There are several ways to create a Task
in C#. Here are a few examples:
Using the Task
Constructor
The most basic way to create a Task
is to use the Task
constructor. This method takes a delegate as an argument, which represents the work that the Task
will perform.
Task task = new Task(() => Console.WriteLine("Hello, World!"));
Using the Task.Factory.StartNew
Method
You can also create a Task
using the Task.Factory.StartNew
method. This method returns a Task
object that represents the work that was started.
Task task = Task.Factory.StartNew(() => Console.WriteLine("Hello, World!"));
Using the Task.Run
Method
The Task.Run
method is a convenience method that creates and starts a Task
in a single call. It returns a Task
object that represents the work that was started.
Task task = Task.Run(() => Console.WriteLine("Hello, World!"));
Waiting for a Task to Complete
Once you have created a Task
, you can wait for it to complete using the Wait
method. This method will block the current thread until the Task
has completed.
Task task = Task.Run(() => Console.WriteLine("Hello, World!"));
task.Wait();
Alternatively, you can use the WaitAny
or WaitAll
methods to wait for multiple tasks to complete.
Task task1 = Task.Run(() => Console.WriteLine("Hello, World!"));
Task task2 = Task.Run(() => Console.WriteLine("Goodbye, World!"));
Task.WaitAll(task1, task2);
Handling Exceptions in Tasks
If an exception occurs while a Task
is running, it will be wrapped in an AggregateException
and thrown when the Task
is waited on or accessed in any other way.
Task task = Task.Run(() => { throw new Exception("Oops!"); });
try {
task.Wait();
} catch (AggregateException ex) {
Console.WriteLine(ex.Message);
}
You can also handle exceptions using the TrySetException
and TrySetCanceled
methods of the TaskCompletionSource
class.
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
try {
throw new Exception("Oops!");
} catch (Exception ex) {
tcs.TrySetException(ex);
}
try {
tcs.Task.Wait();
} catch (AggregateException ex) {
Console.WriteLine(ex.Message);
}
Cancelling a Task
You can cancel a Task
using the CancellationTokenSource
class and the `CancellationTokenparameter of the
Task` constructor.
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
Task task = new Task(() => {
while (!token.IsCancellationRequested) {
Console.WriteLine("Hello, World!");
Thread.Sleep(1000);
}
}, token);
task.Start();
// Cancel the task after 5 seconds
Thread.Sleep(5000);
cts.Cancel();
// Wait for the task to complete
task.Wait();
You can also use the Cancel
method of the CancellationTokenSource
class to cancel a Task
.
CancellationTokenSource cts = new CancellationTokenSource();
Task task = Task.Run(() => {
while (!cts.Token.IsCancellationRequested) {
Console.WriteLine("Hello, World!");
Thread.Sleep(1000);
}
}, cts.Token);
// Cancel the task after 5 seconds
Thread.Sleep(5000);
cts.Cancel();
// Wait for the task to complete
task.Wait();
Returning a Result from a Task
You can use the Task
class to return a result from an asynchronous operation. To do this, you can use the Task
constructor that takes a Func
delegate as an argument. The Func
delegate should return the result of the asynchronous operation.
Task<int> task = new Task<int>(() => {
Thread.Sleep(1000);
return 42;
});
task.Start();
int result = task.Result;
Console.WriteLine(result); // Outputs 42
Alternatively, you can use the Task.Factory.StartNew
or Task.Run
methods to create a Task
that returns a result.
Task<int> task = Task.Run(() => {
Thread.Sleep(1000);
return 42;
});
int result = task.Result;
console.WriteLine(result); // Outputs 42
Continuation Tasks
A continuation Task
is a Task
that is created and started when another Task
completes. You can create a continuation Task
using the ContinueWith
method of the Task
class.
Task task1 = Task.Run(() => {
Thread.Sleep(1000);
Console.WriteLine("Task 1 completed");
});
Task task2 = task1.ContinueWith((t) => {
Console.WriteLine("Task 2 started");
});
task2.Wait();
In this example, task2
will start running when task1
completes.
You can also specify a continuation Task
that runs only if the antecedent Task
completes successfully or faulted.
Task task1 = Task.Run(() => {
Thread.Sleep(1000);
Console.WriteLine("Task 1 completed");
throw new Exception("Oops!");
});
Task task2 = task1.ContinueWith((t) => {
Console.WriteLine("Task 2 started");
}, TaskContinuationOptions.OnlyOnRanToCompletion);
Task task3 = task1.ContinueWith((t) => {
Console.WriteLine("Task 3 started");
}, TaskContinuationOptions.OnlyOnFaulted);
try {
task2.Wait();
} catch (AggregateException ex) {
Console.WriteLine(ex.Message);
}
task3.Wait();
In this example, task2
will only run if task1
completes successfully, and task3
will only run if task1
faults.
Async and Await
The async
and await
keywords in C# allow you to write asynchronous code that is easier to read and write. When you use the async
keyword, you can use the await
keyword to wait for a Task
to complete.
private async void Button_Click(object sender, RoutedEventArgs e)
{
Console.WriteLine("Button clicked");
int result = await LongRunningMethodAsync();
Console.WriteLine(result);
}
private async Task<int> LongRunningMethodAsync()
{
Console.WriteLine("LongRunningMethodAsync started");
await Task.Delay(1000);
Console.WriteLine("LongRunningMethodAsync completed");
return 42;
}
In this example, the LongRunningMethodAsync
method is marked with the async
keyword, and the await
keyword is used to wait for the Task
returned by the Task.Delay
method to complete. When the Button_Click
method is called, it will start the LongRunningMethodAsync
method and wait for it to complete before displaying the result.
Using the async
and await
keywords can make asynchronous programming in C# easier and more efficient.
Conclusion
In this article, we have covered the basics of using the Task
class in C# to perform asynchronous and concurrent operations. We have seen how to create, wait for, and cancel tasks, as well as how to return results and create continuation tasks. We have also seen how the async
and await
keywords can be used to simplify asynchronous programming in C#.