你能让Quartz.Net使用优先级队列吗?

时间:2014-09-04 01:55:16

标签: c# quartz.net

Quart.Net默认只使用作业的优先级,如果它与另一个作​​业同时被触发。但是,如果在不同的时间触发了两个作业,则最早的作业将始终首先在线程池上运行。我有一个场景,但我需要队列中的最高优先级作业始终在下一个线程上运行。有没有一种简单的方法可以使用Quartz.Net执行此操作,或者如果我想完成此操作,我是否必须实现自己的调度程序? (或转向其他技术)。

我想到的具体方案: 定期作业将以高优先级触发,可能会为另一个过程产生一些输出。最大限度地减少等待时间非常重要。但是,我也有时会以较低的优先级同时触发多达数千个工作批次。我担心当这些“批次”被触发时,更重要的工作将等待太长时间才能运行。

有一种简单的方法可以使用Quartz.Net或竞争技术吗?

3 个答案:

答案 0 :(得分:0)

您是否看过触发优先级?我不认为它会在不同的线程上运行它,但它会推动作业在另一个以较低优先级触发的作业之前启动。

Trigger trigger = TriggerBuilder.Create()
   .WithIdentity("TestTrigger")
   .StartNow()
   .WithPriority(10)
   .Build();

答案 1 :(得分:0)

如您所述,Quartz.Net并未直接支持此功能。您可以创建自己的调度程序(参见下文),或者如果您通过更新QRTZ_TRIGGERS表中的NEXT_FIRE_TIME列来使用数据库存储,则可以解决此问题。如果更新NEXT_FIRE_TIME以便按优先级排序它们,则下次调度程序检查计划更改时,将按优先级顺序执行作业。

有一个

public void SignalSchedulingChange(DateTimeOffset? candidateNewNextFireTimeUtc)
更新火灾时间后您可以调用的QuartzScheduler上的

方法,但不幸的是StdScheduler没有公开。如果你正在沿着“我自己的调度程序”路线走下去,你可以直接调用它。否则,在调度程序上调用ScheduleJob方法(以及调度作业)也会调用它,因此您可以在更新下一次触发时间后利用它并安排作业。

答案 2 :(得分:0)

实现此行为的最简单方法是继承StdAdoDelegate(或其中一个特定于数据库的实现)并覆盖GetSelectNextTriggerToAcquireSql,以便在PRIORITY之前按NEXT_FIRE_TIME进行排序代替。

以下类获取默认SQL查询,并在临时空字符占位符的帮助下按子句交换顺序:

public class CustomAdoDelegate : StdAdoDelegate
{
    protected override string GetSelectNextTriggerToAcquireSql(int maxCount)
    {
        return base.GetSelectNextTriggerToAcquireSql(maxCount)
            .Replace(ColumnNextFireTime + " ASC", "\0")
            .Replace(ColumnPriority + " DESC", ColumnNextFireTime + " ASC")
            .Replace("\0", ColumnPriority + " DESC");
    }
}

请记住,如果调度程序被更高优先级的作业所淹没,这可能会导致较低优先级作业的长时间延迟。

我自己没有尝试过,但可能值得考虑采用某种混合方式来平衡PRIORITYNEXT_FIRE_TIME值。例如,您可以从下一个触发时间中以秒为单位减去优先级,以创建一种可变大小的优先级窗口。

order by dateadd(ss, -PRIORITY, NEXT_FIRE_TIME)

因此,优先级= 10的触发器只会在5秒后触发优先级= 5的触发器。只是一个想法。