We have a method which maintains global sequence index of all events in our application. As it is website, it is expected to have such method thread safe. Thread-safe implementation was following:
private static long lastUsedIndex = -1;
public static long GetNextIndex()
{
Interlocked.Increment(ref lastUsedIndex);
return lastUsedIndex;
}
However we noticed that under some not heavy load duplicated indexes appeared in system. Simple test shown that there are about of 1500 duplicates for 100000 iterations.
internal class Program
{
private static void Main(string[] args)
{
TestInterlockedIncrement.Run();
}
}
internal class TestInterlockedIncrement
{
private static long lastUsedIndex = -1;
public static long GetNextIndex()
{
Interlocked.Increment(ref lastUsedIndex);
return lastUsedIndex;
}
public static void Run()
{
var indexes = Enumerable
.Range(0, 100000)
.AsParallel()
.WithDegreeOfParallelism(32)
.WithExecutionMode(ParallelExecutionMode.ForceParallelism)
.Select(_ => GetNextIndex())
.ToList();
Console.WriteLine($"Total values: {indexes.Count}");
Console.WriteLine($"Duplicate values: {indexes.GroupBy(i => i).Count(g => g.Count() > 1)}");
}
}
This can be fixed with following implementation:
public static long GetNextIndex()
{
return Interlocked.Increment(ref lastUsedIndex);
}
However, I do not clearly understand, why first implementation did not work. Can anybody help me describing what is happening in that case?