C# 如何在给定时间处理(动态添加)项?

C# 如何在给定时间处理(动态添加)项?,c#,timer,task,scheduler,priority-queue,C#,Timer,Task,Scheduler,Priority Queue,我有一个以时间戳(将来)为键的函数,当到达时间作为值时,应该调用该函数(/应该处理的项)。我不想在每个项目上都附加一个计时器,因为有很多。我宁愿使用调度程序线程/任务 这样做的好策略是什么? 使用运行调度程序的线程。。。(下面是伪代码) …并在添加元素(到空队列或添加新的第一个元素)时脉冲 或者使用Task.Delay(int,CancellationToken)以某种方式链接(Task.ContinueWith(…))任务?如果新的第一个元素排队,则需要一些逻辑来中止等待;如果没有人在运行

我有一个以时间戳(将来)为键的函数,当到达时间作为值时,应该调用该函数(/应该处理的项)。我不想在每个项目上都附加一个计时器,因为有很多。我宁愿使用调度程序线程/任务

这样做的好策略是什么?


使用运行调度程序的线程。。。(下面是伪代码)

…并在添加元素(到空队列或添加新的第一个元素)时脉冲


或者使用
Task.Delay(int,CancellationToken)
以某种方式链接(
Task.ContinueWith(…)
)任务?如果新的第一个元素排队,则需要一些逻辑来中止等待;如果没有人在运行,则需要一些逻辑来创建新任务。感觉有一个更简单的解决方案我现在还没有


或者使用计时器(非常伪代码,只是为了得到这个想法)

…并在添加元素时更新间隔(如上所述)


我还关心等待方法/计时器的精度:毫秒是相当粗糙的(尽管它可能会工作),有更好的选择吗

因此。。。 这又是一堆问题/想法-抱歉-但是有太多的选择和顾虑,很难得到一个概述。因此,总结一下:为动态进货项目实施(精确)时间安排系统的最佳方法是什么?


我感谢所有的提示和答案!非常感谢。

我建议这样做:

  • 创建一个名为
    TimedItemsConcurrentPriorityQueue的类,该类继承自

  • TimedItemsConcurrentPriorityQueue
    类中实现一个名为ItemReady的事件,该事件根据时间戳在项目准备就绪(待处理)时触发。您可以使用单个计时器,并根据需要通过隐藏
    排队
    插入
    删除
    和其他方法(或者通过修改
    ConcurrentPriorityQueue
    的源并使这些方法虚拟化,以便可以覆盖它们)来更新计时器

  • 实例化
    TimedItemsConcurrentPriorityQueue
    的单个实例,让我们调用变量itemsWaitingToBecomeReady

  • 实例化
    BlockingCollection
    的单个对象,让我们调用变量itemsrady。使用并向其传递一个新的
    ConcurrentPriorityQueue
    (它继承了
    IProducerConsumerCollection

  • 每当在itemsWaitingToBecomeReady中触发事件ItemReady时,您都会取消该项的资格并将其排入itemsReady队列

  • 使用如下新任务的方法处理itemsReady中的项目:


  • 你看了吗?@DanAbramov我觉得我应该看一下,但进入Rx似乎是另一件“大事”(?),所以目前我宁愿找一个没有它的解决方案。或者,您认为这是首选的解决方案吗?(好的,我一定会把它放在我的名单上。)谢谢你的回复。这绝对是件大事。但是,如果异步序列处理多于一个地方,就应该认真考虑它。我喜欢阻塞集合的“无休止的迭代”特性。使用ConcurrentPriorityQueue作为数据源(而不是空构造函数)的想法是什么?这样做
    T
    实际上就是
    KeyValuePair
    ,所有键都不包含任何信息(0或当前时间,具体取决于实现)。(但是,我同意在实现更通用版本的
    itemsrady
    时使用它,它不依赖于键。)@mvondano:在以下场景中,在BlockingCollection中使用ConcurrentPriorityQueue非常有用:假设您正在处理一个需要很长时间才能处理的项目。在处理过程中,您将一个项目添加到itemsWaitingToBecomeReady,该项目也将准备就绪,然后移动到ItemsReady。之后,将另一项添加到itemsWaitingToBecomeReady,但其时间戳早于上一项。此项目也将移动到ItemsReady。在BlockingCollection中使用ConcurrentPriorityQueue时,即使在这种情况下,下一项也是正确的项。我明白了!谢谢你的解释!
    // 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);
    
    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;
    
    Task.Factory.StartNew(() =>
    {
      foreach (var item in itemsReady.GetConsumingEnumerable())
      {
        ...
      }
    }