【问题标题】:Is there a better way to throttle a high throughput job?有没有更好的方法来限制高吞吐量作业?
【发布时间】:2010-03-11 23:27:29
【问题描述】:

我创建了一个简单的类,它显示了我正在尝试做的事情而没有任何噪音。 随意抨击我的代码。这就是我在这里发布它的原因。

public class Throttled : IDisposable
{
    private readonly Action work;
    private readonly Func<bool> stop;
    private readonly ManualResetEvent continueProcessing;
    private readonly Timer throttleTimer;
    private readonly int throttlePeriod;
    private readonly int throttleLimit;
    private int totalProcessed;

    public Throttled(Action work, Func<bool> stop, int throttlePeriod, int throttleLimit)
    {
        this.work = work;
        this.stop = stop;
        this.throttlePeriod = throttlePeriod;
        this.throttleLimit = throttleLimit;
        continueProcessing = new ManualResetEvent(true);
        throttleTimer = new Timer(ThrottleUpdate, null, throttlePeriod, throttlePeriod);
    }

    public void Dispose()
    {
        throttleTimer.Dispose();
        ((IDisposable)continueProcessing).Dispose();
    }

    public void Execute()
    {
        while (!stop())
        {
            if (Interlocked.Increment(ref totalProcessed) > throttleLimit)
            {
                lock (continueProcessing)
                {
                    continueProcessing.Reset();
                }
                if (!continueProcessing.WaitOne(throttlePeriod))
                {
                    throw new TimeoutException();
                }
            }

            work();
        }
    }

    private void ThrottleUpdate(object state)
    {
        Interlocked.Exchange(ref totalProcessed, 0);
        lock (continueProcessing)
        {
            continueProcessing.Set();
        }
    }
}

最新代码

public class Throttled
{
    private readonly Func<bool> work;
    private readonly ThrottleSettings settings;
    private readonly Stopwatch stopwatch;
    private int totalProcessed;

    public Throttled(Func<bool> work, ThrottleSettings settings)
    {
        this.work = work;
        this.settings = settings;
        stopwatch = new Stopwatch();
    }

    private void Execute()
    {
        stopwatch.Start();
        while (work())
        {
            if (++totalProcessed > settings.Limit)
            {
                var timeLeft = (int)(settings.Period - stopwatch.ElapsedMilliseconds);
                if (timeLeft > 0)
                {
                    Thread.Sleep(timeLeft);
                }
                totalProcessed = 0;
                stopwatch.Reset();
                stopwatch.Start();
            }
        }
    }
}

【问题讨论】:

  • 您的代码缺少启动执行的公共方法。
  • 你不需要保护ManualResetEvent:根据文档(msdn.microsoft.com/en-us/library/…),它是线程安全的。
  • @Vlad - 在任何一种情况下,它似乎都打开了主线程在一段时间内不工作的可能性。关于如何解决这个问题的任何想法?
  • 你可以让控制线程不被定时器唤醒,而是等待(其他)事件的信号状态。
  • @Chaos,Timer 类的分辨率与 Sleep 完全相同。是的,大约是 20 毫秒。

标签: c# .net synchronization


【解决方案1】:

首先,我会完全摆脱控制线程,因为它的工作可以在调用work()之前轻松完成。

然后,我会让工作线程与主线程不同,从而为其他任务解除阻塞主线程。接下来,我将添加一个函数来取消处理,这可能会设置一个标志检查工作线程。

编辑:
根据 cmets,我们的目标是限制每个 throttlePeriod 滴答期间的 work() 调用次数。我们可以通过记下秒表中的时间,在throttleLimit 工作操作后进行比较,并在剩余时间休眠时做得更好。这样我们就不再需要计时器线程了。

编辑:(已删除,不正确)
编辑:
我们甚至可以做某种平衡:在throttlePeriod 内,我们计算work() 花费了多少时间,因此我们可以估计所有剩余的work()s 将花费多少时间,然后等待每个两个work()s 平分剩余时间。这将使我们在分配周期开始时不会很快执行所有work(),可能会阻塞数据库。

【讨论】:

  • 有趣,不使用计时器我应该如何精确计时?
  • @ChaosPandion:你需要计时器做什么?好像什么都没记录,只是清除了totalProcessed
  • @ChaosPandion:您的工作代码在 每次 迭代后等待计时器——这就是您所需要的吗?
  • 其实work会在throttlePeriod中被调用throttleLimit次。
  • 好吧,你不需要额外的线程。您可以只使用StopWatch 并测量您用于throttleLimit 迭代的时间。如果时间小于throttleLimit,剩余时间可以Sleep
【解决方案2】:

为什么要油门?当您可以将线程置于较低优先级并让它吸收所有未使用的 CPU 周期以尽可能快地完成其工作而不中断任何较高优先级的工作时,为什么还要 Sleep()?

事实上,为什么不将所有非 UI 线程置于较低的线程优先级,以便您的应用程序整体保持响应?

这里唯一需要注意的是,如果您正在执行 IO - 需要限制磁盘访问以保持其他一切顺利运行。

【讨论】:

  • 我正在限制对数据库的访问以防止锁定。我需要在高峰工作时间运行这项工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-11
  • 1970-01-01
相关资源
最近更新 更多