我有以下课程:
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
的调用会忙 - 等到他们返回。我想通过使用某种操作系统级别的句柄抽象来解决这个问题。
你能建议我怎么做吗?
考虑以下情况:
10 << 30
)Claim(10 << 30)
,它几乎立即返回
Claim(10 << 30)
打了一个相同的电话,然后做了“坏”忙等待1分钟
while(true){/*do nothing*/}
方法中执行类似Claim
循环的操作!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
时出现的忙碌等待情况
如果你有一个完全不同的方法来解决这类问题,或者看到我已经拥有的一些缺陷,我也想听到这个!
答案 0 :(得分:0)
您有几种选择。
例如,您可以通过将活动线程置于休眠状态一段时间来创建更智能的忙等待,因此它并不总是检查您的状况,而是定期检查它。
另一个解决方案是创建一个自定义事件并在活动线程中等待该事件,并且您可以定义一个自定义事件来完成我认为的任务。
答案 1 :(得分:0)
以下是我提出的解决方案:
obj
:Monitor
pollIterval
:在成功完成交易之前调用的速率value
:交易修改的值precondition
:在交易开始时必须为true的可选条件transform
:更改值的操作postcondition
:在交易结束时必须为true的可选条件public static class AtomicHelper
{
public static void LongTransaction(
object obj,
TimeSpan pollInterval,
ref long value,
Func<long, bool> precondition,
Func<long, long> transform,
Func<long, bool> postcondition)
{
while (true)
{
var oldValue = Interlocked.Read(ref value);
if (precondition != null && !precondition(oldValue))
{
Monitor.Wait(obj, pollInterval);
continue;
}
var newValue = transform(oldValue);
if (postcondition != null && !postcondition(newValue))
{
Monitor.Wait(obj, pollInterval);
continue;
}
if (Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue)
{
Monitor.PulseAll(obj);
return;
}
}
}
}
long n = 10;
object obj = new object();
//On many different threads, run this concurrently:
AtomicHelper.LongTransaction(
obj,
TimeSpan.FromSeconds(1),
ref n,
null,
x => x - 1,
x => x >= 0);
Thread.Sleep(TimeSpan.FromSeconds(3));
AtomicHelper.LongTransaction(
obj,
TimeSpan.Zero,
ref n,
null,
x => x + 1,
null);