在 Quartz 中,如何通知作业调度程序正在关闭?

In Quartz, how do I notify a job that the scheduler is shutting down?

我正在使用 Quartz.Net 在 Windows 服务中安排一些较长的 运行 作业。我正在尝试优雅地停止服务,例如准备重新启动时。我需要通知工作,以便他们可以根据离完成的程度来决定是完成还是中止。我已经查看了中断和侦听器,但我似乎无法弄清楚如何传达挂起的关闭。

我也曾尝试将长作业分解为更小的连续作业,但这样做会严重影响性能。

你提到了,但你测试过"void Interrupt()"是否在Scheduler关闭时被调用?

如果这不起作用,我认为没有内置方法。

但如果您想探索所有可能性,请获取 Quartz.Net 的源代码,而不是引用构建的库,将所有 (quartz.net).csproj 添加到您的 .sln 中,然后参考 "by project"...并以这种方式单步执行代码。或者至少查看源代码以收集线索。

那就追这个

namespace Quartz.Core
{

    public class QuartzScheduler : MarshalByRefObject, IRemotableQuartzScheduler
    {
    {
        public virtual void Shutdown(bool waitForJobsToComplete)
        {
            if (shuttingDown || closed)
            {
                return;
            }

这是一个示例 IInterruptableJob。值得一试,看看该方法是否触发。您可能已经尝试过此操作,但由于您没有显示作业的代码,因此这是一个 "what did someone else" 示例。

 public class AnInterruptableJob : IJob, IInterruptableJob
    {

        private bool _isInterrupted = false;

        private int MAXIMUM_JOB_RUN_SECONDS = 10;

        /// <summary> 
        /// Called by the <see cref="IScheduler" /> when a
        /// <see cref="ITrigger" /> fires that is associated with
        /// the <see cref="IJob" />.
        /// </summary>
        public virtual void Execute(IJobExecutionContext context)
        {

            /* See http://aziegler71.wordpress.com/2012/04/25/quartz-net-example/ */

            JobKey key = context.JobDetail.Key;

            JobDataMap dataMap = context.JobDetail.JobDataMap;

            int timeOutSeconds = dataMap.GetInt("TimeOutSeconds");
            if (timeOutSeconds <= 0)
            {
                timeOutSeconds = MAXIMUM_JOB_RUN_SECONDS;
            }

            Timer t = new Timer(TimerCallback, context, timeOutSeconds * 1000, 0);

            Console.WriteLine(string.Format("AnInterruptableJob Start : JobKey='{0}', timeOutSeconds='{1}' at '{2}'", key, timeOutSeconds, DateTime.Now.ToLongTimeString()));

            try
            {
                Thread.Sleep(TimeSpan.FromSeconds(7));
            }
            catch (ThreadInterruptedException)
            {
            }

            if (_isInterrupted)
            {
                Console.WriteLine("Interrupted.  Leaving Excecute Method.");
                return;
            }

            Console.WriteLine(string.Format("End AnInterruptableJob (should not see this) : JobKey='{0}', timeOutSeconds='{1}' at '{2}'", key, timeOutSeconds, DateTime.Now.ToLongTimeString()));

        }

        // Timer t = new Timer(TimerCallback, context, 1000, 0);
        private void TimerCallback(Object o)
        {
            IJobExecutionContext context = o as IJobExecutionContext;

            if (null != context)
            {
                context.Scheduler.Interrupt(context.FireInstanceId);
            }
        }

        public void Interrupt()
        {
            _isInterrupted = true;
            Console.WriteLine(string.Format("AnInterruptableJob.Interrupt called at '{0}'", DateTime.Now.ToLongTimeString()));
        }
    }
}

我认为作业实例无法自行确定调度程序正在关闭。作业(工作者)线程和服务控制线程之间需要一些协调。

这是一个可中断作业示例 class。请注意使用 ManualResetEventSlim 表示已请求中断,并表示 Execute() 方法已相应退出。

注意:此代码基于源下载附带的 Quartz.Server.2010 示例项目代码构建。

/// <summary></summary>
[DisallowConcurrentExecution]
public class DumbInterruptableJob : IInterruptableJob, IDisposable
{
    private static readonly ILog logger = LogManager.GetLogger(typeof(DumbInterruptableJob));

    private ManualResetEventSlim _interruptRequestToken;
    private ManualResetEventSlim _interruptCompleteToken;

    /// <summary></summary>
    public DumbInterruptableJob()
    {
        _interruptRequestToken = new ManualResetEventSlim(false);
        _interruptCompleteToken = new ManualResetEventSlim(false);
    }

    /// <summary></summary>
    /// <param name="context"></param>
    public void Execute(IJobExecutionContext context)
    {
        try
        {
            JobKey key = context.JobDetail.Key;

            logger.Info(m => m("Instance {0} of DumbInterruptableJob is working.", key));

            // The work loop
            for (int i = 0; i < 4; i++)
            {
                if (_interruptRequestToken.IsSet)
                {
                    logger.Info(m => m("Work interrupt requested...Exiting."));  // Breakpoint #1
                    return;
                }

                logger.Info(m => m("Work..."));
                Thread.Sleep(2000);
            }

            logger.Info(m => m("Work complete!"));
        }
        catch (Exception ex)
        {
            logger.Error(m => m(ex.Message));
        }
        finally
        {
            _interruptCompleteToken.Set();
        }
    }

    /// <summary></summary>
    public void Interrupt()
    {
        logger.Info(m => m("Setting interrupt flag..."));
        _interruptRequestToken.Set();

        logger.Info(m => m("Waiting for work thread to stop..."));
        _interruptCompleteToken.Wait();
        logger.Info(m => m("Work thread stopped."));  // Breakpoint #2
    }

    /// <summary></summary>
    public void Dispose()
    {
        _interruptCompleteToken.Dispose();
        _interruptRequestToken.Dispose();
    }
}

下一步是在服务停止时中断(取消)所有 运行ning 个可中断作业。

    public virtual void Stop()
    {
        try
        {
            // Calling Shutdown(false) stops the scheduler from starting new jobs,
            // but the method doesn't block, allowing us to access any running jobs
            // and attempt to cancel (interrupt) them.
            logger.Info(m => m("Shutting down the scheduler..."));
            scheduler.Shutdown(false);

            var interruptableJobs = new List<Task>();

            foreach (var ctx in scheduler.GetCurrentlyExecutingJobs())
            {
                if (ctx.JobInstance is IInterruptableJob)
                {
                    interruptableJobs.Add(Task.Factory.StartNew(() =>
                    {
                        logger.Info(m => m("Waiting for interruptable job {0} to stop...", ctx.JobDetail.Key));
                        scheduler.Interrupt(ctx.JobDetail.Key);
                        logger.Info(m => m("Interruptable job {0} has stopped.", ctx.JobDetail.Key));
                    }));
                }
            }

            if (interruptableJobs.Count > 0)
            {
                logger.Info(m => m("Waiting for all interruptable jobs to stop..."));
                Task.WaitAll(interruptableJobs.ToArray());
                logger.Info(m => m("All interruptable jobs have stopped."));
            }

            logger.Info(m => m("Waiting for all running jobs to complete..."));
            scheduler.Shutdown(true);
            logger.Info(m => m("All running jobs have completed. Scheduler shutdown complete."));  // Breakpoint #3
        }
        catch (Exception ex)
        {
            logger.Error(string.Format("Scheduler stop failed: {0}", ex.Message), ex);
            throw;
        }

        logger.Info("Scheduler shutdown complete");
    }

要查看同步操作,请在注释中指示的位置设置断点,运行它,然后关闭服务(如果 运行ning 从命令行按 Ctrl-C),并观察命令命中断点。