簡體   English   中英

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

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

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

假設我們必須將文件從一個位置復制到另一個位置,並且我們不希望每秒處理超過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;
        }
    }
}

但我需要一種更優雅和防彈的方式。

獲取開始時間,然后在循環中計算應當處理到當前時間的最大文件數,並在您處於領先狀態時進行休眠:

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

}

我會做一個簡單的方法,一次處理五個文件,並使用計時器每秒調用一次。

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

    }
}

請注意,當您嘗試將處理的項目數限制為每秒500次時,上面的代碼仍會運行緊密循環,從而即使在不處理文件時也會對CPU造成負擔。

如果你想要一個真正的限制,你必須將你的邏輯改為定時器或事件驅動,或者使用其中一個Wait *方法來空閑你的進程,同時等待下一次做一些工作的機會。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM