Skip to main content
๐Ÿ“œ WAYPOINT LESSON

Parallel pitfalls and exception handling

โญโญโญ advancedโณ 15 min read๐Ÿ“ Lesson 205 of 265

AggregateException, thread-local accumulation errors, oversubscription, and cancellation of parallel loops.

Parallel pitfalls and exception handling

Exceptions aggregate. A parallel loop may throw on several workers simultaneously; you receive AggregateException carrying them all:

try { Parallel.ForEach(files, f => Process(f)); }
catch (AggregateException ag)
{
    foreach (var ex in ag.Flatten().InnerExceptions) Log(ex);
}

Catching inside the body and swallowing is how partial failures hide โ€” process half the files, report success.

Closure capture is a classic trap. Parallel.For gives you the index as a parameter, so the old for-loop closure bug does not apply โ€” but Parallel.ForEach over a collection whose enumerator is not thread-safe, or shared mutable state in the lambda, will bite. Anything written by two iterations needs atomics or local aggregation.

Oversubscription. Launching 100 Task.Run CPU jobs on 8 cores just multiplies context switches. Bound with MaxDegreeOfParallelism, or partition explicitly. The thread pool's hill-climbing also resents CPU storms โ€” long-running work belongs in LongRunning tasks or dedicated workers, not Task.Run floods.

Cancellation of a parallel loop is cooperative like everything else: pass a token via ParallelOptions, and the loop stops launching new iterations when it fires; bodies must check loopState.ShouldExitCurrentIteration or the token themselves for prompt exit. An unobserved token cancels scheduling only โ€” a common production misunderstanding ("we cancelled it, why is it still eating CPU?").

UI-thread deadlock is an async smell, but Parallel + UI fails too: Parallel.For(...).Wait() on the UI thread is fine, but blocking the UI thread inside a body that itself awaits deadlocks the loop. Keep bodies CPU-only; orchestrate async at the boundary.

โšก Now practice

Ready to Code
Parallel drillsLocal-state aggregation, PLINQ ordering, Amdahl reasoning, and cancellation of long loops.
2 challenges ยท ยท ~45 min