在 Quartz.NET 中,有没有办法设置一个只允许一个 Job 实例运行的属性?

Posted

技术标签:

【中文标题】在 Quartz.NET 中,有没有办法设置一个只允许一个 Job 实例运行的属性?【英文标题】:In Quartz.NET is there a way to set a property that will only allow one instance of a Job to run? 【发布时间】:2011-08-24 11:05:30 【问题描述】:

我有一个每 X 分钟运行一次的服务。如果由于某种不可预见的原因该作业花费的时间超过 X 分钟,我想确保触发器不会启动该作业的第二个实例。

示例场景

我有 Job X,拾取文件并由 Quartz 每 1 分钟触发一次。 作业 X 通常可以在 1 分钟内处理 100 个文件,超过 100 个文件的处理时间将超过 1 分钟。 自上次运行以来,恰好有 150 个文件存在,因此作业 X 启动并开始处理。当达到 1 分钟时,处理了 100 个文件,剩余 50 个文件,作业 X 继续运行。 但是,由于触发器每 1 分钟触发一次,因此会启动 Job X 的第二个实例。 我现在有 2 个作业 X 实例拾取相同的 50 个文件。

有没有办法连接 Quartz.NET 以只允许一个 Job 的 1 个实例?我可以接受第二个触发器等待第一个触发器完成,或者我也可以选择跳过第二个触发器,因为它将在一分钟内再次触发。

我查看了 Quartz API 的 Java 版本,发现了一个属性“DisallowConcurrentExecution”,但在 .NET 版本中没有找到类似的属性。

我的 Quartz.NET 实现代码

公共 IScheduler 调度程序 获取;放; 公共 IJobListener AutofacJobListener get;放; public void Start() var trigger = TriggerUtils.MakeMinutelyTrigger(1); trigger.Name = @"Document Import Trigger"; Scheduler .ScheduleJob(new JobDetail("文档导入", null, typeof(DocumentImportJob)), trigger); Scheduler.AddGlobalJobListener(AutofacJobListener); Scheduler.Start();

【问题讨论】:

如果在前一个计划仍在运行的情况下,计划再次“结束”会发生什么?推迟还是跳过? 跳过或推迟,我都可以选择。 你不能在它运行时简单地设置一个变量或其他东西吗? IE。让工作“开始”,它会立即退出。 在你的函数内部实现逻辑应该比较简单。 我已按照您的建议尝试并标记为答案。但我无法解决问题。请检查github中的问题。我应该怎么做才能解决这个问题? github.com/quartznet/quartznet/issues/469 【参考方案1】:

使用 DisallowConcurrentExecution 属性。

如下声明你的类:

[DisallowConcurrentExecution]
public class SomeTask : IJob


 

失火

"Misfire Instructions 触发器的另一个重要属性是它的 “失火指令”。如果持续触发,则会发生失火 由于调度程序被关闭,“错过”了它的触发时间,或者 因为 Quartz.NET 的线程池中没有可用的线程 执行作业。不同的触发器类型有不同的失火 提供给他们的说明。默认情况下,他们使用“智能策略” 指令 - 它具有基于触发器类型的动态行为和 配置。当调度程序启动时,它会搜索任何 触发失败的持久触发器,然后更新每个触发器 它们基于各自配置的失火指令。什么时候 你开始在你自己的项目中使用 Quartz.NET,你应该 您自己熟悉定义的失火指令 给定的触发器类型,并在其 API 文档中进行了解释。 将提供有关失火指令的更具体信息 在针对每种触发器类型的教程课程中。”

查看这些页面底部的“触发失火指令”信息:

Lesson 5: SimpleTrigger

Lesson 6: CronTrigger

旧 Quartz.NET API 答案:

http://quartznet.sourceforge.net/apidoc/topic142.html:

IStatefulJob 实例遵循与常规稍有不同的规则 IJob 实例。关键的区别是 他们关联的 JobDataMap 是 每次执行后重新持久化 作业,从而为 下一次执行。另一个区别 是不允许有状态的作业 并发执行,这意味着 之前发生的新触发器 IJob.Execute 方法的完成 会延迟。

因此,如下声明您的“工作”类:

class DocumentImportJob : IStatefulJob

   ......
 

为避免延迟任务在作业完成后立即重新触发(当作业花费超过 1 分钟并导致触发器“失火”时),请在创建触发器时执行以下操作(根据使用的触发器类型进行调整):

myJobTrigger.MisfireInstruction = MisfireInstruction.CronTrigger.DoNothing;  

https://www.quartz-scheduler.net/documentation/quartz-2.x/tutorial/more-about-triggers.html

【讨论】:

IStatefulJob 已弃用。而是使用 Job 类定义上方的 [DisallowConcurrentExecution]。这是根据 Quartz.NET 2.2.1。并且可以通过以下与触发器初始化一起使用的方法来处理失火:WithMisfireHandlingInstructionIgnoreMisfires()【参考方案2】:

作为对此答案的更新,在 Quartz.Net 的较新版本中,现在通过应用于作业实施的属性“DisallowConcurrentExecution”来完成:

[DisallowConcurrentExecution]
public class MyJob : IJob  

    ..

对于失火指令,这里是如何做到这一点的:

var trigger = TriggerBuilder.Create()
    .WithSimpleSchedule(ssb => ssb.WithIntervalInMinutes(interval)
        .RepeatForever()
        .WithMisfireHandlingInstructionIgnoreMisfires()
    )
    .Build();

【讨论】:

【参考方案3】:

嗯,一种简单的方法是在某个变量中存储一个标志值,并在进入作业方法时检查该变量。

这样你只会让工作“开始”第二次,它会立即退出而不做任何实际工作。

这是一个例子:

private volatile bool _IsRunning;

...

if (Interlocked.Exchange(ref _IsRunning, true))
    return;
try

    // job code

finally

    _IsRunning = false;

【讨论】:

你的回答很好,但我必须选择@Keith's,因为它正是我想要的,让我让 Quartz 处理一切。 @Mark Yup,我也输入了这样的评论(在他发布他的答案之前),你应该推迟接受我的答案,因为 Quartz.Net 中可能有一些东西会做你想要的,但我不知何故错过了点击“添加评论”按钮。一定是我渴望代表的自我放弃了那部分:)【参考方案4】:

public bool Validar(IJobExecutionContext 上下文)

        if (context.Scheduler.GetCurrentlyExecutingJobs().Any(x => x.FireInstanceId != context.FireInstanceId
         && x.JobDetail.Key == context.JobDetail.Key))
        
            return true;
        
        else
        
            return false;
        

    

【讨论】:

以上是关于在 Quartz.NET 中,有没有办法设置一个只允许一个 Job 实例运行的属性?的主要内容,如果未能解决你的问题,请参考以下文章

Quartz.Net SQLite 没有表 QRTZ_LOCKS

基于进程的Quartz.NET管理系统QuartzService

C# 使用 quartz.net 做定时任务

Easymodbus 在 Quartz.net 类中给出连接错误

Quartz.net使用总结

如何使用 Quartz.Net 开始计划作业?