Claude
Skills
Sign in
Back

csharp-async-patterns

Included with Lifetime
$97 forever

Use when C# asynchronous programming with async/await, Task, ValueTask, ConfigureAwait, and async streams for responsive applications.

Productivity

What this skill does


# C# Async Patterns

Asynchronous programming in C# enables writing responsive applications that
efficiently handle I/O-bound and CPU-bound operations without blocking
threads. The async/await pattern provides a straightforward way to write
asynchronous code that looks and behaves like synchronous code.

## Async/Await Basics

The `async` and `await` keywords transform synchronous-looking code into
state machines that handle asynchronous operations.

```csharp
using System;
using System.Net.Http;
using System.Threading.Tasks;

public class AsyncBasics
{
    // Basic async method
    public async Task<string> FetchDataAsync(string url)
    {
        using var client = new HttpClient();
        // await suspends execution until response received
        string content = await client.GetStringAsync(url);
        return content;
    }

    // Async method without return value
    public async Task ProcessDataAsync()
    {
        await Task.Delay(1000); // Simulate async work
        Console.WriteLine("Processing complete");
    }

    // Multiple awaits
    public async Task<int> CalculateSumAsync()
    {
        int value1 = await GetValueAsync(1);
        int value2 = await GetValueAsync(2);
        int value3 = await GetValueAsync(3);

        return value1 + value2 + value3;
    }

    private async Task<int> GetValueAsync(int id)
    {
        await Task.Delay(100);
        return id * 10;
    }

    // Async with exception handling
    public async Task<string> SafeFetchAsync(string url)
    {
        try
        {
            using var client = new HttpClient();
            return await client.GetStringAsync(url);
        }
        catch (HttpRequestException ex)
        {
            Console.WriteLine($"Request failed: {ex.Message}");
            return string.Empty;
        }
    }

    // Calling async methods
    public async Task DemoAsync()
    {
        // Await the result
        string data = await FetchDataAsync("https://api.example.com");

        // Fire and forget (not recommended)
        _ = ProcessDataAsync();

        // Wait for completion
        await ProcessDataAsync();
    }
}
```

## Task and Task\<T\>

`Task` represents an asynchronous operation and provides methods for
composition, continuation, and error handling.

```csharp
using System;
using System.Threading;
using System.Threading.Tasks;

public class TaskExamples
{
    // Creating tasks
    public void CreateTasks()
    {
        // Task.Run for CPU-bound work
        Task<int> task1 = Task.Run(() =>
        {
            Thread.Sleep(1000);
            return 42;
        });

        // Task.FromResult for already-known values
        Task<int> task2 = Task.FromResult(100);

        // Task.CompletedTask for void operations
        Task task3 = Task.CompletedTask;

        // TaskCompletionSource for manual control
        var tcs = new TaskCompletionSource<string>();
        Task<string> task4 = tcs.Task;
        tcs.SetResult("Done");
    }

    // Task composition
    public async Task<string> ComposeTasks()
    {
        // Sequential execution
        int result1 = await Task1Async();
        int result2 = await Task2Async(result1);

        // Parallel execution
        Task<int> t1 = Task1Async();
        Task<int> t2 = Task2Async(10);
        await Task.WhenAll(t1, t2);

        return $"Results: {t1.Result}, {t2.Result}";
    }

    // Task.WhenAll - wait for all tasks
    public async Task<int[]> WhenAllExample()
    {
        var tasks = new[]
        {
            Task.Run(() => ComputeValue(1)),
            Task.Run(() => ComputeValue(2)),
            Task.Run(() => ComputeValue(3))
        };

        int[] results = await Task.WhenAll(tasks);
        return results;
    }

    // Task.WhenAny - wait for first task
    public async Task<int> WhenAnyExample()
    {
        var task1 = DelayedValue(1000, 1);
        var task2 = DelayedValue(2000, 2);
        var task3 = DelayedValue(500, 3);

        Task<int> completed = await Task.WhenAny(task1, task2, task3);
        return await completed;
    }

    // Cancellation support
    public async Task<string> CancellableOperation(
        CancellationToken cancellationToken)
    {
        for (int i = 0; i < 10; i++)
        {
            cancellationToken.ThrowIfCancellationRequested();

            await Task.Delay(100, cancellationToken);
            Console.WriteLine($"Step {i + 1}");
        }

        return "Completed";
    }

    // Helper methods
    private Task<int> Task1Async() => Task.FromResult(10);
    private Task<int> Task2Async(int value) =>
        Task.FromResult(value * 2);
    private int ComputeValue(int x) => x * x;
    private async Task<int> DelayedValue(int delay, int value)
    {
        await Task.Delay(delay);
        return value;
    }
}
```

## ValueTask and ValueTask\<T\>

`ValueTask` provides better performance for operations that often complete
synchronously, avoiding heap allocations.

```csharp
using System;
using System.Threading.Tasks;

public class ValueTaskExamples
{
    private readonly Dictionary<string, string> _cache =
        new Dictionary<string, string>();

    // ValueTask for cached operations
    public ValueTask<string> GetValueAsync(string key)
    {
        // Synchronous path - no allocation
        if (_cache.TryGetValue(key, out string? value))
        {
            return new ValueTask<string>(value);
        }

        // Asynchronous path
        return new ValueTask<string>(FetchFromDatabaseAsync(key));
    }

    private async Task<string> FetchFromDatabaseAsync(string key)
    {
        await Task.Delay(100); // Simulate database call
        string value = $"Value for {key}";
        _cache[key] = value;
        return value;
    }

    // Converting between Task and ValueTask
    public async ValueTask<int> ConversionExample()
    {
        // ValueTask from Task
        Task<int> task = GetTaskAsync();
        ValueTask<int> valueTask = new ValueTask<int>(task);

        return await valueTask;
    }

    private Task<int> GetTaskAsync() => Task.FromResult(42);

    // ValueTask best practices
    public async Task ValueTaskUsageAsync()
    {
        // Good: await immediately
        string value1 = await GetValueAsync("key1");

        // Bad: storing ValueTask
        // ValueTask<string> vt = GetValueAsync("key2");
        // await vt; // First await
        // await vt; // Second await - WRONG!

        // Good: convert to Task if needed multiple times
        Task<string> task = GetValueAsync("key2").AsTask();
        await task;
        await task; // OK with Task
    }

    // ConfigureAwait with ValueTask
    public async ValueTask ConfigureAwaitExample()
    {
        // Don't capture context (for library code)
        string value = await GetValueAsync("key")
            .ConfigureAwait(false);

        Console.WriteLine(value);
    }
}
```

## ConfigureAwait

`ConfigureAwait` controls whether to capture the synchronization context,
critical for library code and avoiding deadlocks.

```csharp
using System;
using System.Threading.Tasks;

public class ConfigureAwaitExamples
{
    // Library method - use ConfigureAwait(false)
    public async Task<string> LibraryMethodAsync()
    {
        // Don't capture synchronization context
        await Task.Delay(100).ConfigureAwait(false);

        // This continues on thread pool thread
        string result = await GetDataAsync()
            .ConfigureAwait(false);

        return result.ToUpper();
    }

    // UI method - use default (or ConfigureAwait(true))
    public async Task UpdateUIAsync()
    {
        string data = await LoadDataAsync();

        // This continues on UI thread
        // Can safely update UI controls
        Console.WriteLine($"Data: {data}");
    }

    // Avoiding deadlocks
    public class DeadlockExample
    {
        // This can deadlock in synchronous context
        public string BadSync()
        {
            // DON'T DO THIS
            return Ge

Related in Productivity