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

时间:2010-11-03 07:51:39

标签: c# .net

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

让我们说我们必须将文件从一个位置复制到另一个位置,我们不希望每秒处理超过5个文件。

请看看我在做什么

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;
        }
    }
}

但我需要更优雅和防弹的方式。

5 个答案:

答案 0 :(得分:6)

获取开始时间,然后在循环中计算应当处理到当前时间的最大文件数,如果你处于领先地位则休眠:

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);
  }

}

这样,如果某些操作需要更长时间,代码就会赶上来。如果你一秒钟只进行三次操作,那么下一秒就可以进行七次操作。

请注意,我使用的是UtcNow而不是Now,以避免每年发生两次的令人讨厌的跳跃。

编辑:

另一个选择是测量一个操作所花费的时间,并在其余的时间段内休眠:

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);
  }

}

答案 1 :(得分:5)

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

答案 2 :(得分:4)

Sumee

您可以使用受限制的生产者/消费者队列。它会有一个后台线程,它以一定的间隔运行,处理你的文件。您可以在文件名到达时将其排队,并且限制的出列操作(Action)将出列。这是我的意思的一个例子:

    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();

    }
}

答案 3 :(得分:3)

请注意,当您尝试将处理的项目数限制为每秒500次时,上面的代码仍会运行紧密循环,因此即使不处理文件也会对CPU造成负担。

如果您想要真正的限制,您必须将您的逻辑更改为计时器或事件驱动,或者使用其中一种Wait *方法使您的进程空闲,同时等待下一次工作的机会。

答案 4 :(得分:3)