gpt4 book ai didi

.net - Interlocked.Increment 和返回增量值

转载 作者:行者123 更新时间:2023-12-04 17:44:07 33 4
gpt4 key购买 nike

我们有一个方法可以维护我们应用程序中所有事件的全局序列索引。由于它是网站,因此预计具有线程安全的这种方法。线程安全的实现如下:

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);
}

但是,我不太明白,为什么第一个执行没有奏效。谁能帮我描述一下在这种情况下发生了什么?

最佳答案

如果它在您的原始示例中起作用,您也可以说它适用于一般情况

Interlocked.Increment(ref someValue);

// Any number of operations

return someValue;

要做到这一点,您必须消除 Increment 之间的所有并发(包括并行性、重入性、抢占式代码执行...)和返回。更糟糕的是,您需要确保即使 someValue用于 return 和 Increment 之间,它不会以任何方式影响返回。换句话说 - someValue必须不可能在两个语句之间更改(不可变)。

您可以清楚地看到,如果是这种情况,您就不需要 Interlocked.Increment首先 - 你只要做 someValue++ . Interlocked的整个目的和其他原子操作是为了确保操作要么立即(原子地)发生,要么根本不发生。特别是,它可以保护您免受任何类型的指令重新排序(通过 CPU 优化或通过在两个逻辑 CPU 上并行运行的多个线程,或在单个 CPU 上被抢占)。但仅限于原子操作内。后续阅读 someValue不是同一个原子操作的一部分(它本身是原子的,但是两个原子操作也不会使总和成为原子的)。

但是您不是要进行“任意数量的操作”,对吗?事实上,你是。因为有其他线程与您的线程异步运行 - 您的线程可能被这些线程之一抢占,或者线程可能真正在多个逻辑 CPU 上并行运行。

在真实环境中,您的示例提供了一个不断增加的字段(因此它比 someValue++ 好一点),但它没有为您提供唯一的 id,因为您正在阅读的只是 someValue在某个不确定的时刻。如果两个线程同时尝试进行增量,两者都会成功( Interlocked.Increment 是原子的),但它们也会从 someValue 读取相同的值。 .

这并不意味着你总是想使用 Interlocked.Increment 的返回值。 - 如果您对增量本身更感兴趣,而不是增量值。一个典型的例子可能是一种廉价的分析方法——每个方法调用可能会增加一个共享字段,然后不时读取该值,例如平均每秒调用次数。

关于.net - Interlocked.Increment 和返回增量值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42137152/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com