Besides locks and `Interlocked`, describe how to use thread-local storage in a `Parallel.For` loop to safely calculate an aggregate sum without causing data races.

.NET interview question for Advanced practice.

Answer

You can use an overload of Parallel.For that supports thread-local state. This allows each thread to maintain its own private subtotal during the loop's execution. Once all threads have finished their portion of the work, a final step merges all the private subtotals into the final grand total. This pattern avoids contention entirely during the parallel processing phase. The overload has the following key parameters: 1. localInit: A function delegate that initializes the local state for each thread (e.g., creates a long subtotal initialized to 0). 2. body: The main loop body. It receives the current index, a ParallelLoopState object, and the thread-local subtotal variable. It performs its calculation and updates its local subtotal. 3. localFinally: A function delegate that is called once per thread after it has finished all its iterations. This is where you atomically add the thread's local subtotal to the global total. csharp long total = 0; object lockObj = new object(); Parallel.For(0, 10000, // range () = 0L, // localInit: initialize thread-local subtotal to 0 (i, loopState, subtotal) = // body: executed for each item { subtotal += i; // operate on thread-local variable return subtotal; }, (subtotal) = Interlocked.Add(ref total, subtotal) // localFinally: atomically merge subtotals );

Explanation

Using thread-local variables is a highly efficient pattern for parallel aggregation because each thread works on its own private data, eliminating all synchronization overhead during the loop's execution. Synchronization is only required once at the very end to combine the results.

Related Questions