我们有一个方法可以在我们的应用程序中维护所有事件的全局序列索引。因为它是网站,所以预计这种方法线程安全。线程安全的实现是以下几点:Interlocked.Increment和返回递增值
private static long lastUsedIndex = -1;
public static long GetNextIndex()
{
Interlocked.Increment(ref lastUsedIndex);
return lastUsedIndex;
}
然而,我们注意到,一些不重负载下重复出现的索引系统。简单的测试表明,对于100000次迭代,大约有1500个重复。
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)}");
}
}
这可以固定实现如下:
public static long GetNextIndex()
{
return Interlocked.Increment(ref lastUsedIndex);
}
不过,我并不了解清楚,为什么第一个实现没有工作。任何人都可以帮助我描述在这种情况下发生的事情吗?
因为'lastUsedIndex'可能已经通过对获得结果并将其返回给调用者之间的'Interlocked.Increment'的另一个调用进行更新。 –
标准线程竞争错误,另一个线程也可能会在'return lastUsedIndex;'之前递增该变量;'小的可能性不是零。代码执行'[读取修改写入]读取'。您从Interlocked获得的原子性保证只会持续用于括号内的操作。你必须使用'lock [read modify write read]'来使其安全。理解,汉斯帕斯特, –
RB。你们中的任何人都可以把它写成答案,所以它会被完全回答的问题吗? –