Start Debugging

The async and concurrency cheat sheet

Every await decision, and the deadlock it avoids.

This pillar collects everything on the site about asynchronous and concurrent code - the async/await decisions in C#, cancellation, the .NET 11 runtime-async work, the locking and channel primitives, and the deadlocks and exceptions each wrong turn produces.

What to read first

Start with the daily pair: async void vs async Task and .Result vs .Wait() vs GetAwaiter().GetResult() vs await, then the deadlock blocking causes and migrating a legacy codebase to async all the way up. ConfigureAwait(false) vs default in .NET 11 answers whether that still matters. Tracing Android ANRs back to async void handlers shows what a stray one costs in production.

For return types, what ValueTask is and when it’s worth it, IEnumerable vs IAsyncEnumerable vs IQueryable, and returning a Task directly vs an async passthrough cover the signature choices; stuck in sync code, wait on a ValueTask without allocating. For running work in parallel, Parallel.ForEach vs Parallel.ForEachAsync vs Task.WhenAll decides, with the AggregateException WhenAll throws as the gotcha. For coordination, lock vs Monitor vs SemaphoreSlim vs System.Threading.Lock and Channels instead of BlockingCollection are the primitives. Below the locks, Volatile.Read vs Volatile.ReadBarrier picks the fence, and Thread.Sleep vs Task.Delay vs SpinWait the way to wait. For shutdown, propagating a CancellationToken and CancelAfter timeouts are the pair.

What’s on this page

The list below auto-collects posts tagged with any of: async, concurrency, threading - which pulls in the Dart and Flutter async posts too. Newest first.

Companion pillars: C# 14 features and the .NET 11 tracker.

Index (47 posts)

2026 / 10

  • unawaited_futures vs discarded_futures in Dart: which lint catches which un-awaited call

    unawaited_futures only looks inside async bodies, discarded_futures only inside sync bodies, and both miss arrow functions, tear-offs, and collection literals. Measured case by case on Dart 3.12.2 and Flutter 3.44.8, with the analysis_options.yaml I recommend.

  • Thread.Sleep vs Task.Delay vs SpinWait for waiting in C#

    Use await Task.Delay for any timed wait in async code, Thread.Sleep only on a dedicated thread you own, and SpinWait only for sub-microsecond waits inside lock-free code. To wait for a condition, use a signal (ManualResetEventSlim, SemaphoreSlim, TaskCompletionSource) instead of any of the three. Measured on .NET 10.0.10.

  • How to tail a log file in C# while another process is still writing to it

    Open the log with FileShare.ReadWrite | FileShare.Delete, seek to the end, poll for new bytes, decode them with your own UTF-8 Decoder instead of StreamReader, and handle truncation and rotation explicitly. A tested tail -F implementation for .NET 10 and .NET 11.

  • How to wait for a ValueTask synchronously in a non-async method without allocating

    Check IsCompleted first and read the result through GetAwaiter().GetResult(), which costs 0 bytes. Only fall back to blocking when the ValueTask is still pending, and do that with a cached event and UnsafeOnCompleted instead of AsTask(), which allocates 72 to 208 bytes per call. Measured on .NET 10 and .NET 11 RC1.

  • Volatile.Read vs Volatile.ReadBarrier in .NET 10

    Volatile.Read is an acquire load of one location. Volatile.ReadBarrier, new in .NET 10, is a fence that gives every earlier read acquire semantics. Use Volatile.Read for flags and published references, and ReadBarrier when you need a batch of plain or non-atomic reads to complete before the next memory access, as in a seqlock.

2026 / 09

2026 / 08

2026 / 07

2026 / 06

  • How to use BuildContext safely after an await in Flutter

    Capture what you need from the context before the await, then guard the resume with if (context.mounted) return. Here is the full pattern, the lint that enforces it, and the edge cases it misses.

  • What is ValueTask<T> and when is it worth it?

    ValueTask and ValueTask<T> are structs that let an async method return a result without heap-allocating a Task when it completes synchronously. The win is one fewer allocation on hot paths that usually finish without awaiting. The cost is a strict await-once contract. Here is what the type actually is, how it works, and the narrow set of cases where it earns its keep.

  • What is IAsyncEnumerable<T> and when should I use it?

    IAsyncEnumerable<T> is the interface for asynchronous streams: a sequence whose elements arrive over time and each one may require an await. Here is what it actually is, how await foreach and yield drive it, and the rule for when to reach for it over Task<List<T>>.

  • Migrate from ValueTask<T> back to Task<T>: when and why (.NET 11, C# 14)

    A practical checklist for reverting ValueTask and ValueTask<T> return types to Task and Task<T>, what breaks at the call sites, how to verify each change, and how to know whether the swap was ever worth it.

  • Fix: ObjectDisposedException: Cannot access a disposed context instance

    Your fire-and-forget task captured a request-scoped DbContext that the DI scope already disposed. Resolve a fresh context inside the task with IServiceScopeFactory or IDbContextFactory.

2026 / 05

2026 / 04

All pillars Home