Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
C# Synchronization: lock, Interlocked, SemaphoreSlim and Concurrent Collections
Why sold++ never reaches a million: race conditions and the check-then-act race. The rules of lock and why you can't await inside it, atomic counters and CompareExchange with Interlocked, the SemaphoreSlim pattern for an async cache, ConcurrentDictionary factories that run more than once and the Lazy<T> fix.
C# Parallelism: Parallel.For, Parallel.ForEachAsync, PLINQ and When to Use Task.Run
Why nothing finishes when you pass an async lambda to Parallel.ForEach, concurrency versus parallelism, Parallel.For and thread-local aggregation, bounded concurrency with .NET 6 Parallel.ForEachAsync, ordering in PLINQ, BenchmarkDotNet on when parallelism pays off and when it costs you, and where Task.Run belongs.
C# IAsyncEnumerable, await foreach and Async Dispose
Collecting books from a paged API with Task<List<T>> leaves the first element until last; with an async iterator, yield return and await together let the first page flow right away. What await foreach expands to and MoveNextAsync's ValueTask, EnumeratorCancellation and CS8425, the finally that runs on break, IAsyncDisposable and await using, DeserializeAsyncEnumerable and streaming in ASP.NET Core.
C# ValueTask: When to Use It Instead of Task, and Its Rules
An async method that returns synchronously from a cache allocates 72 bytes per call with Task<int>, and nothing with ValueTask<int>. The Debug versus Release measurement gap, the runtime's small-result cache, the cached and truly asynchronous paths in BenchmarkDotNet, the real InvalidOperationException from awaiting a ValueTask twice on a Channel, AsTask, and when to choose ValueTask.
C# async void and Fire-and-Forget: Lost Exceptions
Why an exception from an async void method crashes the process without ever reaching try/catch, and how async Task fixes it. Using async void correctly in event handlers, hidden async void with List.ForEach, the silently lost failure of an un-awaited task and UnobservedTaskException, the CS4014 warning, a safe Forget extension that logs the failure, and the scope trap in ASP.NET Core.
C# Deadlocks, SynchronizationContext and ConfigureAwait
We reproduce the .Result deadlock for real with our own single-threaded SynchronizationContext and stop it with a watchdog. How await captures the context and Posts to it, why it does not hang in a console app or ASP.NET Core but risks thread pool starvation, async all the way, ConfigureAwait(false) in library code, the thread that does not change on a completed task, and the price of wrapping with Task.Run.
C# CancellationToken: Cancellation, Timeouts and Linked Tokens
Cancellation in .NET is cooperative: a method that takes the token and never checks it does not stop. The roles of CancellationTokenSource and CancellationToken, passing the token down, ThrowIfCancellationRequested in CPU work, the task that ends Faulted when Task.Run does not get the token, CancelAfter and HttpClient.Timeout, telling user cancellation from timeouts with linked tokens, stopping WhenAny's losers, and API design.
Concurrent Work in C# with Task.WhenAll and Task.WhenAny
Async code is not concurrent by itself: awaiting independent work one by one adds the durations up. Starting first and waiting later, getting ordered results with Task.WhenAll, the trap of starting tasks twice with LINQ Select, WhenAll waiting for everyone after a failure, first answer and timeouts with WhenAny, losers that keep running, and processing tasks as they complete.
Exceptions in C# Async: AggregateException and Errors in WhenAll
An exception in an async method goes into the task and is thrown with its original type at the await. Throwing parameter checks at the call, the difference between Wait, GetResult and await, reaching every error and partial results from WhenAll, an error order that differs between two overloads, AggregateException.Handle and unobserved task exceptions.
How C# async/await Works: The State Machine and Continuations
await does not always wait: on a finished task the code continues on the same thread. What the async keyword actually does, the GetAwaiter/IsCompleted/OnCompleted steps, a working hand-written state machine, looking at the compiler-generated type with reflection (a class in Debug, a struct in Release), which thread runs the continuation, and the awaitable pattern.
C# Task and Task<T>: A Promise That Starts Hot
An async method starts running the moment it is called; await does not start work, it waits for its result. The task life cycle and Status values, creating tasks with Task.FromResult and TaskCompletionSource, awaiting the same task twice, starting work first and awaiting later, and the Task<Task> trap of Task.Factory.StartNew.
C# Threads and the ThreadPool: A Short History of Async from APM and EAP to TPL
Foreground versus background threads, why the Thread class cannot carry results or errors, and a real measurement of 1000 threads against the thread pool. Then .NET's road to async: APM's Begin/End pair, EAP's Completed event, TPL's Task and the async/await that arrived with C# 5.