I am working on a multi-threaded application using Task.WhenAll() to process multiple requests concurrently. However, I’m encountering unexpected behavior where shared data is being corrupted, possibly due to a race condition.
Here’s the code:
public class DataProcessor
{
private static int _counter = 0;
public async Task ProcessDataAsync()
{
var tasks = new List<Task>();
for (int i = 0; i < 1000; i++)
{
tasks.Add(Task.Run(() => IncrementCounter()));
}
await Task.WhenAll(tasks);
}
private void IncrementCounter()
{
_counter++;
}
}
After running this code, the value of _counter is often less than 1000, indicating that some increments might be missed. How can I fix this issue and ensure thread-safe operations in this multi-threaded context?
I have no idea for it now
The issue here is that _counter++ is not thread-safe. It seems like a simple increment operation, but in reality, it involves three steps:
In a multi-threaded environment, multiple threads can read and increment _counter simultaneously, leading to missed updates and race conditions. This is why the final value of _counter is often less than expected.
Use Interlocked for Atomic Operations: The Interlocked class provides atomic operations for variables that are shared across threads. Interlocked.Increment ensures that the increment operation is atomic and thread-safe. It avoids race conditions without needing explicit locks.
Interlocked Class API - Microsoft
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
public class DataProcessor
{
// Shared counter, incremented atomically
private static int _counter = 0;
public async Task ProcessDataAsync()
{
const int taskCount = 1000;
var tasks = new List<Task>(taskCount);
// Add tasks to increment the counter
for (int i = 0; i < taskCount; i++)
{
tasks.Add(Task.Run(IncrementCounter));
}
// Wait for all tasks to complete
await Task.WhenAll(tasks);
Console.WriteLine($"Final Counter Value: {_counter}");
}
private void IncrementCounter()
{
Interlocked.Increment(ref _counter); // Atomic increment
}
// Entry point to test the code
public static async Task Main(string[] args)
{
var processor = new DataProcessor();
await processor.ProcessDataAsync();
}
}