我有以下类(class):
public class AtomicLong
{
private long initial;
private long value;
public AtomicLong(long value = 0)
{
this.initial = value;
this.value = value;
}
public class Handle : IDisposable
{
private AtomicLong source;
private long amount;
public Handle(AtomicLong source, long amount)
{
this.source = source;
this.amount = amount;
}
public void Dispose()
{
if (source == null)
return;
Interlocked.Add(ref source.value, amount);
source = null;
}
}
public Handle Claim(long amount)
{
if (amount > initial)
throw new ArgumentOutOfRangeException("amount", amount, "Must be no more than the initial amount.");
if (amount < 0)
throw new ArgumentOutOfRangeException("amount", amount, "Must be nonnegative.");
while (true)
{
var oldValue = Interlocked.Read(ref value);
var newValue = oldValue - amount;
if (newValue >= 0 &&
oldValue == Interlocked.CompareExchange(ref value, newValue, oldValue))
{
return new Handle(this, amount);
}
}
}
}
这个用法的一个例子是我可以有一个 AtomicLong unusedMemory
它表示一组工作程序可用的当前内存字节数。 (这并不意味着接近精确 - 这只是一个粗略的衡量标准。)然后我在一堆不同的工作线程上执行此操作:
while (true)
{
var unitOfWork = WaitForUnitOfWork();
long requiredMemory = unitOfWork.RequiredMemory;
using (var handle = unusedMemory.Claim(requiredMemory))
{
//wait until requireMemory can be claimed from unusedMemory
//do work with reserved memory, represented by handle
//when handle disposes, memory is released back to the unusedMemory
}
}
我的 AtomicLong
的问题类是对 Claim
的调用会忙着等他们回来。我想通过使用某种操作系统级等待句柄抽象来解决这个问题。
你能建议我该怎么做吗?
动机
考虑以下场景:
- unusedMemory 初始值为 10GB (
10 << 30
) - 100 个工作线程
- 10 个工作单元,每个工作需要 10GB 的空间和 1 分钟的执行时间
- 第一个 worker 调用
Claim(10 << 30)
它几乎立即返回- 它开始执行将在 1 分钟后完成的工作
- 其他 9 名 worker 对
Claim(10 << 30)
发出了相同的调用并做一个“坏”忙等待 1 分钟- 9 个线程做类似
while(true){/*do nothing*/}
的事情在Claim
中循环方法! - 大量不必要的 CPU 使用
- 9 个线程做类似
- 其余的工作人员 (90) 在
WaitForUnitOfWork()
中执行“良好”的操作系统级等待方法
重点: Claim
只有在请求 amount
时才“便宜”的内存实际上是可以申请的。如果不是,则忙等待直到它可用。
为了完全清楚,在 Claim
中方法,我指出了造成所有差异的确切表达式 ( newValue >= 0
):
while (true)
{
var oldValue = Interlocked.Read(ref value);
var newValue = oldValue - amount;
if (newValue >= 0 && // <--------------------------- THIS IS THE PROBLEM
oldValue == Interlocked.CompareExchange(ref value, newValue, oldValue))
{
return new Handle(this, amount);
}
}
问题不在于是否Interlocked.CompareExchange
会很贵-我知道它很便宜。问题是关于如何处理在 amount
时发生的忙等待。来电者想要 Claim
当前大于 amount
在AtomicLong
.
如果您有解决此类问题的完全不同的方法,或者发现我已有的方法存在一些缺陷,我也很想听听!
最佳答案
您有多种选择。
例如,您可以通过让事件线程在给定的时间间隔内休眠来创建更智能的忙等待,这样它并不总是检查您的情况,而是定期检查。
另一种解决方案是创建自定义事件并在您的事件线程中等待该事件,我相信您可以定义一个自定义事件来完成您的任务。
关于c# - 对数字的原子等待然后执行操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16678383/