【发布时间】:2017-06-27 11:44:43
【问题描述】:
我们有一个方法可以维护应用程序中所有事件的全局序列索引。由于它是网站,因此预计会有这种方法线程安全。线程安全实现如下:
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;之前增加变量@ 小几率,而不是零。代码是[read modify write] read。您从 Interlocked 获得的原子性保证仅适用于括号中的操作。您必须使用lock [read modify write read]以确保安全。 -
RB,Hans Passant,明白了。你们中的任何人都可以把它写成答案,这样它就会得到完全回答的问题吗?
-
您得到了很好的提示,因此您可以编写自己的答案。请务必使用它们。
标签: .net multithreading increment interlocked-increment