Task Basics
The Task class is the foundation of modern async programming in C#. It represents an asynchronous operation that may or may not return a value.
Task vs Thread
| Thread | Task |
|---|---|
| Low-level OS construct | High-level abstraction |
| Manual lifecycle management | Automatic management |
| No built-in return value | Task<T> returns values |
| Exception crashes app | Exceptions are captured |
| Limited composition | Rich composition (WhenAll, etc.) |
Creating Tasks
Task.Run()
The most common way to run code on a background thread:
csharp1// Run work on thread pool2Task task = Task.Run(() =>3{4 Console.WriteLine("Running on background thread");5});67// Wait for completion8task.Wait();
Task with Return Value
csharp1// Task<T> returns a value2Task<int> task = Task.Run(() =>3{4 Thread.Sleep(1000); // Simulate work5 return 42;6});78// Get the result (blocks until complete)9int result = task.Result;10Console.WriteLine($"Result: {result}");
Task.Factory.StartNew()
For advanced scenarios with more options:
csharp1Task task = Task.Factory.StartNew(2 () => DoWork(),3 CancellationToken.None,4 TaskCreationOptions.LongRunning, // Use dedicated thread5 TaskScheduler.Default6);
Task States
A task progresses through several states:
csharp1Task task = Task.Run(() => Thread.Sleep(1000));23Console.WriteLine(task.Status); // WaitingToRun or Running4task.Wait();5Console.WriteLine(task.Status); // RanToCompletion
Possible states:
Created- Task created but not startedWaitingForActivation- Awaiting schedulingWaitingToRun- Scheduled, waiting for threadRunning- Currently executingRanToCompletion- Completed successfullyFaulted- Threw an exceptionCanceled- Was canceled
Checking Task Status
csharp1Task<int> task = Task.Run(() => ComputeValue());23// Check various properties4bool isComplete = task.IsCompleted; // Finished (any reason)5bool isSuccess = task.IsCompletedSuccessfully; // No errors6bool isFaulted = task.IsFaulted; // Threw exception7bool isCanceled = task.IsCanceled; // Was canceled89// Get exception if faulted10if (task.IsFaulted)11{12 AggregateException ex = task.Exception;13}
Waiting for Tasks
Wait()
Blocks the current thread:
csharp1Task task = Task.Run(() => DoWork());2task.Wait(); // Blocks until complete34// With timeout5bool completed = task.Wait(5000); // Wait max 5 seconds
Result Property
Blocks and returns the value:
csharp1Task<string> task = Task.Run(() => FetchData());2string data = task.Result; // Blocks until complete
Warning: Using Wait() or Result can cause deadlocks in UI/ASP.NET contexts. Prefer await.
Completed Tasks
For returning already-completed tasks:
csharp1// Already completed with value2Task<int> completed = Task.FromResult(42);34// Already completed (no value)5Task done = Task.CompletedTask;67// Already failed8Task failed = Task.FromException(new InvalidOperationException());910// Already canceled11Task canceled = Task.FromCanceled(new CancellationToken(true));
Task vs Task
csharp1// Task - no return value (like void)2Task task1 = Task.Run(() =>3{4 Console.WriteLine("No return value");5});67// Task<T> - returns a value8Task<int> task2 = Task.Run(() =>9{10 return 42; // Returns int11});1213int value = await task2; // Get the value
Complete Example
csharp1Console.WriteLine("Starting tasks...");23// Create multiple tasks4Task<int> task1 = Task.Run(() =>5{6 Thread.Sleep(1000);7 return 10;8});910Task<int> task2 = Task.Run(() =>11{12 Thread.Sleep(500);13 return 20;14});1516Task<int> task3 = Task.Run(() =>17{18 Thread.Sleep(1500);19 return 30;20});2122// Wait for all and get results23Task.WaitAll(task1, task2, task3);2425int total = task1.Result + task2.Result + task3.Result;26Console.WriteLine($"Total: {total}"); // 60
Key Takeaways
Taskrepresents an async operationTask<T>returns a value of type TTask.Run()executes code on the thread poolTask.Wait()and.Resultblock (avoid in async contexts)- Tasks capture exceptions instead of crashing
- Use
Task.FromResult()for pre-completed tasks