在 Quartz.net、运行 中一次一个作业实例

In Quartz.net, run one instance of a job at a time

我提到了以下问题,但没有帮助我解决问题。

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

https://github.com/quartznet/quartznet/issues/469

对于 CronTrigger,在调度程序中使用了以下内容 cs.WithMisfireHandlingInstructionDoNothing()

HelloJob 中应用了以下属性
DisallowConcurrentExecution.

代码怎么了?
在 Execute 方法中,我设置了断点。根据我的代码,每个执行方法将在 10 秒内执行。

打到第一个破发点后,我又等了31秒。然后我删除了断点并执行了代码,根据我的预期,应该只执行一次以进行另一次尝试。

But the execute method executed 3 times (3*10 seconds) within another 10 seconds.

如何解决?

调度程序代码。

ISchedulerFactory schedFact = new StdSchedulerFactory();
IScheduler sched = schedFact.GetScheduler();
sched.Start();

// define the job and tie it to our HelloJob class
IJobDetail job = JobBuilder.Create<HelloJob>()
    .WithIdentity("myJob", "group1")
    .Build();

// Trigger the job to run now, and then every 40 seconds
ITrigger trigger = trigger = TriggerBuilder.Create()
    .WithIdentity("trigger3", "group1")
    .WithCronSchedule("0/10 * * * * ?",cs=>cs.WithMisfireHandlingInstructionDoNothing())
    .ForJob("myJob", "group1")
    .Build();

TriggerKey key = new TriggerKey("trigger3", "group1");
sched.ScheduleJob(job, trigger);

作业执行代码。

[DisallowConcurrentExecution]
public class HelloJob : IJob
{        
     public static int count = 1;
    public void Execute(IJobExecutionContext context)
    {
         Console.WriteLine(count+" HelloJob strted On." + DateTime.Now.ToString());
        if (count == 1)
            Thread.Sleep(TimeSpan.FromSeconds(30));

        Interlocked.Increment(ref count);
    }
}

============================================= =======================

解决方案

无需联动或人工管理

Quartz 已经设计成只完成第一个计划,下一个计划开始。

所以我们不用担心它会运行并发。

例如(像我这样的人:-p),调度程序安排了 10 分钟。

但是如果我们在execute方法中复制下面的代码,你可以看到, 第一次,需要 20 分钟才能完成。 第二次,需要15分钟才能完成。

10 分钟后将不会有下一个时间表开始。

   var startTime = DateTime.UtcNow;

            if (count == 1)
            {
                while (DateTime.UtcNow - startTime < TimeSpan.FromSeconds(20))
                {
                    // Execute your loop here...
                }
            }
            else if (count > 1)
            {
                while (DateTime.UtcNow - startTime < TimeSpan.FromSeconds(15))
                {
                    // Execute your loop here...
                }
            }
            count++;

几天前我遇到了同样的问题。结果是我不得不取消工作安排,这是触发。 我正在使用此方法在我的应用程序结束时执行此操作。

private void StopAllJobs()
{
     // construct a scheduler factory
     ISchedulerFactory schedFact = new StdSchedulerFactory();
     // get a scheduler, start the schedular before triggers or anything else
     sched = schedFact.GetScheduler();
     var executingJobs = sched.GetCurrentlyExecutingJobs();
     foreach (var job in executingJobs)
     {
         sched.Interrupt(job.JobDetail.Key);
         sched.UnscheduleJob(job.Trigger.Key);
         sched.DeleteJob(job.JobDetail.Key);
     }
     sched.Clear();
}

希望对您有所帮助。

你的情况是,在第一个长达 30 秒的作业执行期间,Quartz 安排了另外三个执行。这就是为什么在长时间执行后看到三个短执行。它们不是同时执行。一个接一个,但没有延迟。 Quartz 就是这样设计的。

[DisallowConcurrentExecution]
public class HelloJob : IJob
{
    public static int count = 1;
    public void Execute(IJobExecutionContext context)
    {
        Console.WriteLine("HelloJob strted On." + DateTime.UtcNow.Ticks);
        if (count == 1)
            Thread.Sleep(TimeSpan.FromSeconds(30));

        Interlocked.Increment(ref count);
    }
}

并且输出:

Ctrl+C to exit.
HelloJob strted On.636280218500144776
HelloJob strted On.636280218800201691
HelloJob strted On.636280218800211705
HelloJob strted On.636280218800222083
HelloJob strted On.636280218900009629
HelloJob strted On.636280219000000490

看,时间戳不同,下一个在第一个完成后开始。

如果您想避免这种行为,您应该增加作业之间的延迟,或者按照@BrunoFerreira 的建议手动处理此作业的计划。

只需使用这样的东西:

        private static readonly SemaphoreSlim Lock = new SemaphoreSlim(1, 1);

        if (await Lock.WaitAsync(new TimeSpan(0, 0, 30)))
        {
           ...
        }

我们有几份工作,这很好用。