我有一个带有时间戳(将来)的(concurrent) priority queue 作为键和一个应该被调用的函数(/应该处理的项)作为值。我不想为每个项目附加一个计时器,因为它有很多。我宁愿选择调度程序线程/任务。
这样做的好策略是什么?
运行调度程序的线程...(后跟伪代码)
// scheduler
readonly object _threadLock = new object();
while (true)
{
if(queue.Empty)
{
Monitor.Wait(_threadLock);
}
else
{
var time = GetWaitingTimeForNextElement();
if(time > 0)
Monitor.Wait(_threadLock, time);
else
// dequeue and process element
}
}
...在添加元素时(以空队列或添加新的第一个元素)发出脉冲?
// element enqueued
Monitor.Pulse(_threadLock);
或者使用Task.ContinueWith(...)
以某种方式链接(Task.Delay(int, CancellationToken )
)任务?如果新的第一个元素入队,则需要一些逻辑来中止等待,或者如果没有人正在运行则需要创建新任务。感觉就像有一个更简单的解决方案我现在没有得到。 :)
或使用计时器(非常伪代码,只是为了得到这个想法)......
System.Timers.Timer x = new System.Timers.Timer().Start();
x.Elapsed += (sender, args) =>
{
// dequeue and process item(s)
x.Interval = GetWaitingTimeForNextElement(); // does this reset the timer anyway?
}
...并在添加元素时更新间隔(如上所述)。
// element enqueued
x.Interval = updatedTime;
我也关注等待方法/定时器的精确度:毫秒是非常粗糙的(虽然它可能有效)是否有更好的选择?
这又是一堆问题/想法 - 对不起 - 但是有很多选择和担忧很难得到概述。总结一下:为动态传入的项目实施(精确)时间安排系统的最佳方法是什么?。
我感谢所有的提示和答案!非常感谢。
答案 0 :(得分:1)
我建议这样做:
创建一个名为TimedItemsConcurrentPriorityQueue<TKey, TValue>
的类,该类继承自ConcurrentPriorityQueue<TKey, TValue>
。
在TimedItemsConcurrentPriorityQueue<TKey, TValue>
类中实现一个名为ItemReady的事件,根据时间戳,当项目准备好(进行处理)时会触发该事件。您可以根据需要使用单个计时器并根据需要更新计时器,方法是根据需要隐藏Enqueue
,Insert
,Remove
和其他方法(或者通过修改ConcurrentPriorityQueue<TKey, TValue>
的来源和将这些方法设为虚拟,以便覆盖它们。)
实例化TimedItemsConcurrentPriorityQueue<TKey, TValue>
的单个实例,让我们调用该变量itemsWaitingToBecomeReady。
实例化BlockingCollection<T>
的单个对象,让我们称之为变量itemsReady。使用constructor that takes an IProducerConsumerCollection<T>
并向其传递ConcurrentPriorityQueue<TKey, TValue>
的新实例(它继承IProducerConsumerCollection<KeyValuePair<TKey,TValue>>
)
每当在itemsWaitingToBecomeReady中触发事件ItemReady时,您将该项目解除并将其排入itemsReady。
使用BlockingCollection<T>.GetConsumingEnumerable
方法处理itemsReady中的项目,使用如下新任务:
Task.Factory.StartNew(() =>
{
foreach (var item in itemsReady.GetConsumingEnumerable())
{
...
}
}