c# - 对数字的原子等待然后执行操作

标签 c# multithreading .net-4.0 synchronization atomic

我有以下类(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 使用
  • 其余的工作人员 (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当前大于 amountAtomicLong .


如果您有解决此类问题的完全不同的方法,或者发现我已有的方法存在一些缺陷,我也很想听听!

最佳答案

您有多种选择。

例如,您可以通过让事件线程在给定的时间间隔内休眠来创建更智能的忙等待,这样它并不总是检查您的情况,而是定期检查。

另一种解决方案是创建自定义事件并在您的事件线程中等待该事件,我相信您可以定义一个自定义事件来完成您的任务。

您可以阅读有关事件的更多信息 here .您可以阅读有关自定义事件创建的信息 here .

关于c# - 对数字的原子等待然后执行操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16678383/

相关文章:

c# - .Net Socket 增强以支持 ACK 和聊天?

c# - 具有 volatile 或内存屏障的双锁

c# - 区分 getter-only 属性和表达式主体属性?

.net - .NET 控制台应用程序中的 Thread.CurrentPrincipal

c# - Visual Studio 中一个项目的两个站点

c# - ASP.net 同时连接到两个数据库?

c++ - 多维数组初始化: Any benefit from Threading?

asp.net - 在 IIS 7.5 Express 中使用 ASP.Net 预加载器

c# - 将文本字段添加到 UIAlertView

c# - 根据子类指定基类抽象方法的返回类型