这是我的问题:
我有数以千计的传感器进行投票。每个传感器都有一个轮询间隔(不固定,而是随时间变化)。
所以我需要一个接一个地有效地轮询它们 为此,我可以使用优先级队列(使用poll-timestamps作为键)。
问题在于传感器可以被忽略:
例如,如果需要每15分钟轮询一次传感器,并且每5分钟需要轮询大量传感器,那么优先级队列可能会优先于第一个传感器。
所以我可以使用几个优先级队列而不是一个,但是如何选择下一个传感器进行轮询的问题仍然存在。
对我的问题有什么理想的解决方案?
感谢。
答案 0 :(得分:4)
如果您使用预定的轮询时间作为优先级,您可能无法跟上轮询,但您不会遇到饥饿问题,而某些传感器从未被轮询。
Java DelayQueue
非常适合这种情况。将预定时间存储在Delayed
实例中(基于System.nanoTime()
),然后实施getDelay()
以计算其与当前System.nanoTime()
之间的差异。
由于DelayedQueue
是并发队列,因此多个使用者可以执行任务并运行它们,并独立地重新安排新任务。这将有助于跟上负载并维护您的日程安排,因为在轮询慢速传感器时,另一个线程可以等待下一个任务有资格执行。
假设您有两个传感器:传感器A每秒轮询一次,传感器B每3秒轮询一次,队列按计划轮询时间排序。
time 0: A polled, rescheduled for time 1 B polled, rescheduled for time 3 time 1: A polled, rescheduled for time 2 time 2: A polled, rescheduled for time 3 time 3: A polled, rescheduled for time 4 B polled, rescheduled for time 6
时间3的轮询顺序是不确定的,但是B肯定会在任何传感器在4时间安排之前进行轮询。
来自OP评论:
传感器不会退出队列,而是更新其密钥
然后你做错了。优先级队列没有密钥,但如果有,则不支持密钥突变。
队列是生产者添加元素的集合,消费者从中删除元素。如果其内容未发生变化,请使用其他集合。
如果有时候有太多的工作要按计划完成所有事情,你有两个基本选择:做所有事情,接受一些会迟到,或者跳过一些工作,以便完成最多及时完成重要任务。哪种方法取决于您的应用程序,但第一种方法在您可能永远无法赶上的意义上是脆弱的。
如果您必须完成所有工作,但不关心何时,您可能会使用两个优先级。第一个是时间表,如已经描述的那样。第二个是"重要性" metric,用于确定可以执行的任务的优先级。
少量工作线程(可能只有一个)消耗DelayedQueue
,根据其日程安排等待任务。但是,这些工作人员不是直接执行任务,而是根据其重要性将任务简单地放入另一个BlockingQueue
。较大的工作池会占用此队列,首先执行最重要的任务。
如果平均负载大于可用容量,则此队列将继续增长。否则,所有工作最终都将完成,您可以设计任何您喜欢的策略,以便在任务准备好执行后对其进行优先级排序。
可能是后期样本毫无价值,最好是跳过轮询而不是进一步落后。在这种情况下,一个宽限期"可以为每个任务指定。当它出现在DelayQueue
之外时,检查当前时间是否在执行的宽限期内。如果是,请继续执行任务。否则,丢弃任务并获得下一个任务。宽限期可以是轮询频率的函数,较长的周期具有较长的宽限期,而跳过高频任务有助于快速清除队列而不会长时间丢失样本。
你可以结合"宽限期"第一种方法的想法,给最重要的任务提供了按时执行的最佳机会,但是当它太迟而无法使用它们以便赶上时,跳过任务。
所有这些策略对于执行任务的单个线程仍然有用,但是当一个线程串行执行相对长时间运行的任务(如轮询物理传感器)时,保持按计划是一个相当高的顺序。
即使处理器是单核并且一次只执行一个线程,它仍然会通过上下文切换支持线程调度,以便可以在另一个任务上进行,而其他任务在I / O上被阻塞。这样可以更好地保留所需的时间表。
答案 1 :(得分:1)
以下可能是解决您问题的方法:
1)如果您需要处理多种优先级,则建议使用单个优先级队列。基本上,您需要在等待一段时间后将低优先级作业的优先级提升到更高优先级的作业。通过这种方式,您可以处理低优先级作业无限期挨饿的情况,但如果存在大量低优先级作业,则可能暂时使高优先级作业匮乏。
2)当可用的优先级作业有限时,建议使用多个队列。现在,每个队列都保存特定的优先级作业。因此,对于从低优先级队列中取出的每个作业,您可以从更高优先级的队列中轮询多个作业。这有助于降低低优先级工作的饥饿率。