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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5984220/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 15:34:24  来源:igfitidea点击:

In Quartz.NET is there a way to set a property that will only allow one instance of a Job to run?

.netquartz.net

提问by Mark

I have a service that will run every X minutes. If that job takes longer than X minutes for some unforeseen reason I want to make sure that the trigger doesn't kick off a second instance of this job.

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

Sample Scenario

示例场景

  • I have Job X, picks up files and is triggered by Quartz every 1 minute.
  • Job X can typically process 100 files in 1 minute, anything over 100 files will take longer than 1 minute.
  • Since the last run time, 150 files happen to be out there so Job X kicks off and begins processing. When 1 minute is reached 100 files were processed, 50 files remain, and Job X continues to run.
  • However a second instance of Job X is kicked off because the trigger fires every 1 minute.
  • I now have 2 instances of Job X picking up the same 50 files.
  • 我有工作 X,拿起文件,每 1 分钟由 Quartz 触发。
  • 作业 X 通常可以在 1 分钟内处理 100 个文件,超过 100 个文件的任何文件都需要超过 1 分钟。
  • 自上次运行以来,恰好有 150 个文件存在,因此作业 X 开始并开始处理。达到 1 分钟时,处理了 100 个文件,剩余 50 个文件,作业 X 继续运行。
  • 但是,由于触发器每 1 分钟触发一次,因此启动了作业 X 的第二个实例。
  • 我现在有 2 个 Job X 实例获取了相同的 50 个文件。

Is there a way to wire up Quartz.NET to only allow 1 instance of a Job? I'm OK with the second trigger waiting for the first to complete or I'm also OK with an option for it to skip the second trigger since it will be triggered again in a minute.

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



I took a look at the Java version of Quartz API and found a property 'DisallowConcurrentExecution' but didn't find one similar in the .NET version.



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

My Code for the Quartz.NET Implementation

我的 Quartz.NET 实现代码

public IScheduler Scheduler { get; set; }
public IJobListener AutofacJobListener { get; set; }

public void Start()
{
var trigger = TriggerUtils.MakeMinutelyTrigger(1);
trigger.Name = @"Document Import Trigger";

Scheduler.ScheduleJob(new JobDetail("Document Import", null, typeof(DocumentImportJob)), trigger);
Scheduler.AddGlobalJobListener(AutofacJobListener);
Scheduler.Start();
}

回答by Keith Blows

Use the DisallowConcurrentExecution attribute.

使用 DisallowConcurrentExecution 属性。

Declare your class as follows:

声明你的类如下:

[DisallowConcurrentExecution]
public class SomeTask : IJob
{

} 

Misfires

失火

"Misfire Instructions Another important property of a Trigger is its "misfire instruction". A misfire occurs if a persistent trigger "misses" its firing time because of the scheduler being shutdown, or because there are no available threads in Quartz.NET's thread pool for executing the job. The different trigger types have different misfire instructions available to them. By default they use a 'smart policy' instruction - which has dynamic behavior based on trigger type and configuration. When the scheduler starts, it searches for any persistent triggers that have misfired, and it then updates each of them based on their individually configured misfire instructions. When you start using Quartz.NET in your own projects, you should make yourself familiar with the misfire instructions that are defined on the given trigger types, and explained in their API documentation. More specific information about misfire instructions will be given within the tutorial lessons specific to each trigger type."

您应该熟悉在给定触发器类型上定义并在其 API 文档中解释的失火指令。有关失火指令的更多具体信息将在针对每种触发器类型的教程课程中提供。”

Check out the "trigger misfire instructions" information at the bottom of these pages:

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

Lesson 5: SimpleTrigger

第 5 课:简单触发器

Lesson 6: CronTrigger

第 6 课:CronTrigger

Old Quartz.NET API answer:

旧的 Quartz.NET API 答案:

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

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

IStatefulJobinstances follow slightly different rules from regular IJob instances. The key difference is that their associated JobDataMap is re-persisted after every execution of the job, thus preserving state for the next execution. The other difference is that stateful jobs are not allowed to Execute concurrently, which means new triggers that occur before the completion of the IJob.Execute method will be delayed.

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

So, declare your 'Job' class as follows:

因此,按如下方式声明您的“Job”类:

class DocumentImportJob : IStatefulJob
{
   ......
} 

To avoid delayed tasks re-firing immediately after job completes (when the job takes more than 1 minute and causes a trigger 'misfire'), do the following when creating your trigger(s) (adjust depending on the trigger type used):

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

myJobTrigger.MisfireInstruction = MisfireInstruction.CronTrigger.DoNothing;  

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

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

回答by Bittercoder

As an update to this answer, in newer versions of Quartz.Net this is now done via an attribute "DisallowConcurrentExecution" you apply to your job implementation:

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

[DisallowConcurrentExecution]
public class MyJob : IJob  
{
    ..
}

And for the misfire instruction, here is how to do that:

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

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

回答by Lasse V. Karlsen

Well, one simple way to do it would be to just store a flag value in a variable somewhere, and check the variable upon entrance to the job method.

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

That way you would just let the job "start" a second time, it would just exit immediately without doing any real work.

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

Here's an example:

下面是一个例子:

private volatile bool _IsRunning;

...

if (Interlocked.Exchange(ref _IsRunning, true))
    return;
try
{
    // job code
}
finally
{
    _IsRunning = false;
}

回答by Christian Mercado

public bool Validar(IJobExecutionContext context) {

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;
        }

    }