Async/concurrency safety helpers to prevent deadlocks, handle fire-and-forget tasks, manage cancellation tokens, and provide safe async patterns.
$ dotnet add package DotNetAsyncSafePrevents async/await deadlocks, handles fire-and-forget tasks safely, and manages concurrency issues in .NET applications.
async/await deadlocks when mixing sync and async code.Result or .Wait() blocks threads and causes deadlocksConfigureAwait(false)CancellationToken not properly propagated through async chainsParallel.ForEach with async operations incorrectlyasync void methods are lostlock() statements in async code causes deadlocksSemaphoreSlim in async contextsdotnet add package DotNetAsyncSafe
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"
);
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());
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");
}
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();
}
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;
}
}
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)
);
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()
);
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);
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}"
);
}
}
TaskHelper.RunSync() instead of .Result or .Wait()AsyncLock instead of lock() in async codeParallelAsyncHelper.ForEachAsync() to prevent thread pool starvationTaskHelper.FireAndForget()TaskHelper.WithTimeoutAsync()TaskHelper.FireAndForget() - Safely execute fire-and-forget tasksTaskHelper.RunSync() - Safely run async code synchronouslyTaskHelper.WithTimeoutAsync() - Add timeout to async operationsAsyncLock - Async-compatible lock for critical sectionsParallelAsyncHelper.ForEachAsync() - Parallel processing with concurrency limitsCancellationTokenHelper - Utilities for cancellation token managementBackgroundTaskHost - Host for background tasks that survive shutdown