Ana*_*ick 5 .net multithreading increment interlocked-increment
我们有一个方法可以维护应用程序中所有事件的全局序列索引.因为它是网站,所以预计这种方法线程安全.线程安全实现如下:
private static long lastUsedIndex = -1;
public static long GetNextIndex()
{
Interlocked.Increment(ref lastUsedIndex);
return lastUsedIndex;
}
Run Code Online (Sandbox Code Playgroud)
但是我们注意到在一些不重负载的情况下,系统中出现了重复索引.简单测试显示,对于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)}");
}
}
Run Code Online (Sandbox Code Playgroud)
这可以通过以下实现来解决:
public static long GetNextIndex()
{
return Interlocked.Increment(ref lastUsedIndex);
}
Run Code Online (Sandbox Code Playgroud)
但是,我不清楚,为什么第一次实施不起作用.任何人都可以帮我描述那种情况下发生的事情吗?
如果它在原始示例中起作用,您也可以说它适用于一般情况
Interlocked.Increment(ref someValue);
// Any number of operations
return someValue;
Run Code Online (Sandbox Code Playgroud)
为此,您必须消除Increment和返回之间的所有并发(包括并行性,重新执行,抢先代码执行......).更糟糕的是,你需要确保即使someValue在回报和回报之间使用Increment,也不会以任何方式影响回报.换句话说 - someValue两个语句之间必须无法改变(不可变).
您可以清楚地看到,如果是这种情况,您首先不需要Interlocked.Increment- 您只需要这样做someValue++.Interlocked原子操作和其他原子操作的目的是确保操作一次(原子地)或根本不发生.特别是,它可以防止任何类型的指令重新排序(通过CPU优化或通过在两个逻辑CPU上并行运行的多个线程,或者在单个CPU上预先绑定).但只能在原子操作中.随后的读取someValue是不相同的原子操作的一部分(这是原子本身,而是两个原子操作是没有的总和原子为好).
但是你不是想做"任意数量的操作",不是吗?实际上,你是.因为有其他线程相对于您的线程异步运行 - 您的线程可能被其中一个线程抢占,或者线程可能真正在多个逻辑CPU上并行运行.
在实际环境中,你的例子提供了一个不断增长的领域(所以它是略高于更好someValue++),但它不为你提供的唯一ID,因为你正在阅读都是someValue在某个时间不确定的时刻.如果两个线程同时尝试执行增量,则两者都将成功(Interlocked.Increment 是原子的),但它们也将从中读取相同的值someValue.
这并不意味着您总是希望使用返回值Interlocked.Increment- 如果您对增量本身更感兴趣,而不是增量值.一个典型的例子可能是一个廉价的分析方法 - 每个方法调用可能会增加一个共享字段,然后偶尔读取一次该值,例如每秒平均调用次数.
| 归档时间: |
|
| 查看次数: |
1180 次 |
| 最近记录: |