简体   繁体   English

你如何限制每秒的操作次数?

[英]How would you limit the number of operations per second?

How would you limit the number of operations per second? 你如何限制每秒的操作次数?

Lets say we have to copy files from one location to another and we don't want more than 5 files to be processed per second. 假设我们必须将文件从一个位置复制到另一个位置,并且我们不希望每秒处理超过5个文件。

Please see what I am doing 请看看我在做什么

private static string currentStamp;
private static int processedInCurrentStamp = 0;

private static void Main(string[] args)
{
    currentStamp = DateTime.Now.ToString("{0:d/M/yyyy HH:mm:ss}");
    Run();
}

private static void Run()
{
    for (int i = 0; i < Int32.MaxValue; i++)
    {
        string s = DateTime.Now.ToString("{0:d/M/yyyy HH:mm:ss}");
        if (currentStamp.Equals(s))
        {
            if (processedInCurrentStamp < 5)
            {
                ProcessItem();
                processedInCurrentStamp++;
            }
        }
        else
        {
            Console.WriteLine("{0} ::: {1}", currentStamp, processedInCurrentStamp);
            currentStamp = s;
            processedInCurrentStamp = 0;
        }
    }
}

But I need a more elegant and bullet proof way. 但我需要一种更优雅和防弹的方式。

Get the starting time, and then in the loop calculate the maximum number of files that should be processed up to the current time, and sleep if you are ahead: 获取开始时间,然后在循环中计算应当处理到当前时间的最大文件数,并在您处于领先状态时进行休眠:

DateTime start = DateTime.UtcNow;

int i = 1;
while (i <= 100) {

  int limit = (int)((DateTime.UtcNow - start).TotalSeconds * 5.0);

  if (i <= limit) {

    Console.WriteLine(i);
    i++;

  } else {
    Thread.Sleep(100);
  }

}

This way the code will catch up if some operations take longer. 这样,如果某些操作需要更长时间,代码将会赶上来。 If you only get three operations one second, it can do seven the next second. 如果你一秒钟只进行三次操作,那么它可以在下一秒内完成七次操作。

Note that I am using UtcNow instead of Now to avoid the nasty jump in time that happens twice a year. 请注意,我使用的是UtcNow而不是Now以避免每年发生两次的令人讨厌的跳跃。

Edit: 编辑:

Another alternative is to measure the time that an operation takes, and sleep the rest of it's time slot: 另一种方法是测量操作所花费的时间,并在其余的时间段内休眠:

for (int i = 1; i <= 100; i++ ) {

  DateTime start = DateTime.UtcNow;

  Console.WriteLine(i);

  int left = (int)(start.AddSeconds(1.0 / 5.0) - DateTime.UtcNow).TotalMilliseconds;
  if (left > 0) {
    Thread.Sleep(left);
  }

}

我会做一个简单的方法,一次处理五个文件,并使用计时器每秒调用一次。

Sumee Sumee

You could use a throttled producer/ consumer queue. 您可以使用受限制的生产者/消费者队列。 It would have a background thread which runs at an interval, processing your file. 它会有一个后台线程,它以一定的间隔运行,处理你的文件。 You could enqueue the file names as they arrive and the throttled dequeue action (Action) would dequeue. 您可以在文件名到达时将其排队,并且限制的出列操作(Action)将出列。 Here is an example of what I mean: 这是我的意思的一个例子:

    public class ThrottledQueue<T> : IDisposable where T : class
{
    readonly object _locker = new object();
    readonly List<Thread> _workers;
    readonly Queue<T> _taskQueue = new Queue<T>();
    readonly Action<T> _dequeueAction;
    readonly TimeSpan _throttleTimespan;
    readonly Thread _workerThread;
    /// <summary>
    /// Initializes a new instance of the <see cref="SuperQueue{T}"/> class.
    /// </summary>
    /// <param name="millisecondInterval">interval between throttled thread invokation</param>
    /// <param name="dequeueAction">The dequeue action.</param>
    public ThrottledQueue(int millisecondInterval, Action<T> dequeueAction)
    {
        _dequeueAction = dequeueAction;

        // Create and start a separate thread for each worker
        _workerThread = new Thread(Consume) { IsBackground = true, Name = string.Format("ThrottledQueue worker") };
        _workerThread.Start();
        _throttleTimespan = new TimeSpan(0,0,0,0,millisecondInterval);

    }


    /// <summary>
    /// Enqueues the task.
    /// </summary>
    /// <param name="task">The task.</param>
    public void EnqueueTask(T task)
    {
        lock (_locker)
        {
            _taskQueue.Enqueue(task);
        }
    }

    /// <summary>
    /// Consumes this instance.
    /// </summary>
    void Consume()
    {
        while (true)
        {
            T item = default(T);
            lock (_locker)
            {
                Monitor.Wait(_locker, _throttleTimespan);
                if (_taskQueue.Count != 0) 
                    item = _taskQueue.Dequeue();
            }
            if (item == null) return;

            // run actual method
            _dequeueAction(item);
        }
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    public void Dispose()
    {
        // Enqueue one null task per worker to make each exit.
        EnqueueTask(null);

        _workerThread.Join();

    }
}

Note that while you are trying to limit the number of items processed to 500 per second, the code above still runs a tight loop, thus putting a load on the CPU even when not processing a file. 请注意,当您尝试将处理的项目数限制为每秒500次时,上面的代码仍会运行紧密循环,从而即使在不处理文件时也会对CPU造成负担。

If you want a real throttling, you have to change your logic to be timer- or event-driven, or use one of the Wait* methods to idle your process while waiting for the next opportunity to do some work. 如果你想要一个真正的限制,你必须将你的逻辑改为定时器或事件驱动,或者使用其中一个Wait *方法来空闲你的进程,同时等待下一次做一些工作的机会。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM