您如何限制每秒的操作次数?
让我们说我们必须将文件从一个位置复制到另一个位置,我们不希望每秒处理超过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;
}
}
}
但我需要更优雅和防弹的方式。
答案 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)