DotNetAsyncSafe 1.0.0

dotnet add package DotNetAsyncSafe --version 1.0.0
                    
NuGet\Install-Package DotNetAsyncSafe -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="DotNetAsyncSafe" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="DotNetAsyncSafe" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="DotNetAsyncSafe" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add DotNetAsyncSafe --version 1.0.0
                    
#r "nuget: DotNetAsyncSafe, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package DotNetAsyncSafe@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=DotNetAsyncSafe&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=DotNetAsyncSafe&version=1.0.0
                    
Install as a Cake Tool

DotNetAsyncSafe

Prevents async/await deadlocks, handles fire-and-forget tasks safely, and manages concurrency issues in .NET applications.

Problems Solved

  • Deadlocks in ASP.NET: async/await deadlocks when mixing sync and async code
  • Thread freezing: Using .Result or .Wait() blocks threads and causes deadlocks
  • Silent failures: Fire-and-forget tasks fail without logging or handling
  • Missing ConfigureAwait: Confusion about when to use ConfigureAwait(false)
  • Cancellation not flowing: CancellationToken not properly propagated through async chains
  • Background tasks killed: Background tasks terminated during application shutdown
  • No timeout handling: Tasks run indefinitely without timeout protection
  • Thread pool starvation: Too many concurrent async operations exhaust thread pool
  • Parallel.ForEach misuse: Using Parallel.ForEach with async operations incorrectly
  • Race conditions: Shared state accessed without proper synchronization
  • Lost exceptions: Exceptions in async void methods are lost
  • Incorrect async locks: Using lock() statements in async code causes deadlocks
  • SemaphoreSlim misuse: Improper use of SemaphoreSlim in async contexts
  • CPU-bound on async threads: Running CPU-intensive work on async threads blocks I/O
  • Sync/async mixing: Mixing synchronous and asynchronous APIs incorrectly

Installation

dotnet add package DotNetAsyncSafe

Quick Start

1. Safe Fire-and-Forget Tasks

Problem: Fire-and-forget tasks fail silently, making debugging impossible.

using DotNetAsyncSafe;

// ❌ BAD: Exceptions are lost
Task.Run(async () => await ProcessDataAsync());

// ✅ GOOD: Exceptions are logged
TaskHelper.FireAndForget(
    async () => await ProcessDataAsync(),
    logger,
    "ProcessData"
);

2. Prevent Deadlocks with Safe Synchronous Waits

Problem: Using .Result or .Wait() causes deadlocks in ASP.NET.

// ❌ BAD: Causes deadlock
var result = SomeAsyncMethod().Result;

// ✅ GOOD: Safe synchronous execution
var result = TaskHelper.RunSync(() => SomeAsyncMethod());

3. Task Timeouts

Problem: Tasks run indefinitely, blocking resources.

// ❌ BAD: No timeout protection
await LongRunningOperationAsync();

// ✅ GOOD: Automatic timeout with exception
try
{
    var result = await TaskHelper.WithTimeoutAsync(
        LongRunningOperationAsync(),
        TimeSpan.FromSeconds(30)
    );
}
catch (TimeoutException ex)
{
    logger.LogError(ex, "Operation timed out");
}

4. Async Lock for Critical Sections

Problem: Using lock() in async code causes deadlocks.

// ❌ BAD: lock() doesn't work with async
lock (_lockObject)
{
    await UpdateSharedResourceAsync();
}

// ✅ GOOD: Async-compatible lock
var asyncLock = new AsyncLock();
using (await asyncLock.LockAsync())
{
    await UpdateSharedResourceAsync();
}

5. Background Tasks That Survive Shutdown

Problem: Background tasks are killed during application shutdown.

// ✅ GOOD: Register background task that survives shutdown
services.AddBackgroundTask<MyBackgroundService>();

public class MyBackgroundService : IBackgroundTask
{
    public async Task ExecuteAsync(CancellationToken cancellationToken)
    {
        while (!cancellationToken.IsCancellationRequested)
        {
            await ProcessQueueAsync();
            await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken);
        }
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        // Cleanup logic
        return Task.CompletedTask;
    }
}

6. Prevent Thread Pool Starvation

Problem: Too many concurrent async operations exhaust the thread pool.

// ❌ BAD: All operations run concurrently
var tasks = items.Select(item => ProcessItemAsync(item));
await Task.WhenAll(tasks);

// ✅ GOOD: Limit concurrency to prevent starvation
await ParallelAsyncHelper.ForEachAsync(
    items,
    maxConcurrency: 10, // Process max 10 at a time
    async (item, ct) => await ProcessItemAsync(item)
);

7. CPU-Bound Work on Background Threads

Problem: CPU-intensive work blocks async threads, preventing I/O operations.

// ❌ BAD: Blocks async thread
var result = await Task.Run(() => ExpensiveCalculation());

// ✅ GOOD: Explicitly run on background thread
var result = await ParallelAsyncHelper.RunCpuBoundAsync(
    () => ExpensiveCalculation()
);

8. Cancellation Token Helpers

Problem: Cancellation tokens not properly linked or validated.

// ✅ GOOD: Create linked cancellation tokens
var cts = CancellationTokenHelper.CreateLinkedToken(
    httpContext.RequestAborted,
    timeoutCts.Token
);

await ProcessRequestAsync(cts.Token);

Real-World Example

public class OrderProcessor
{
    private readonly AsyncLock _lock = new();
    private readonly ILogger<OrderProcessor> _logger;

    public async Task ProcessOrdersAsync(IEnumerable<Order> orders)
    {
        // Process orders with concurrency limit
        await ParallelAsyncHelper.ForEachAsync(
            orders,
            maxConcurrency: 5,
            async (order, ct) =>
            {
                // Use async lock for critical section
                using (await _lock.LockAsync(ct))
                {
                    await ValidateOrderAsync(order, ct);
                    await SaveOrderAsync(order, ct);
                }
            }
        );
    }

    public void ProcessOrderInBackground(Order order)
    {
        // Fire-and-forget with proper error handling
        TaskHelper.FireAndForget(
            async () => await SendNotificationAsync(order),
            _logger,
            $"SendNotification-{order.Id}"
        );
    }
}

Best Practices

  1. Always use TaskHelper.RunSync() instead of .Result or .Wait()
  2. Use AsyncLock instead of lock() in async code
  3. Limit concurrency with ParallelAsyncHelper.ForEachAsync() to prevent thread pool starvation
  4. Always log fire-and-forget tasks using TaskHelper.FireAndForget()
  5. Set timeouts on long-running operations using TaskHelper.WithTimeoutAsync()
  6. Use background task host for tasks that must survive shutdown

API Reference

  • TaskHelper.FireAndForget() - Safely execute fire-and-forget tasks
  • TaskHelper.RunSync() - Safely run async code synchronously
  • TaskHelper.WithTimeoutAsync() - Add timeout to async operations
  • AsyncLock - Async-compatible lock for critical sections
  • ParallelAsyncHelper.ForEachAsync() - Parallel processing with concurrency limits
  • CancellationTokenHelper - Utilities for cancellation token management
  • BackgroundTaskHost - Host for background tasks that survive shutdown
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 109 12/29/2025

Initial release. Provides async/concurrency safety helpers to prevent deadlocks, handle fire-and-forget tasks, manage cancellation tokens, and provide safe async patterns.