2016-02-02 3 views
0

Я использую Quartz.Net для планирования некоторых длительных рабочих заданий в службе Windows. Я пытаюсь остановить услугу изящно, например, при подготовке к перезагрузке. Мне нужно сообщить задания, чтобы они могли решить, заканчивать или прервать, исходя из того, насколько они близки к завершению. Я смотрел на прерывания, а также на слушателей, но я не могу понять, как сообщить о ожидающем завершении работы.В Quartz, как я могу уведомить задание, которое планировщик отключает?

Я также пытался сломать длинные рабочие места на более мелкие, последовательные рабочие места, но при этом я получаю серьезный удар по производительности.

ответ

0

Я не думаю, что экземпляр задания может самостоятельно определять, что планировщик отключается. Требуется некоторая координация между потоком задания (рабочего) и потоком управления сервисом.

Вот пример прерывания класса заданий. Обратите внимание на использование 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(); 
    } 
} 

Следующий шаг - прервать (отменить) все выполняемые прерывистые задания при остановке службы.

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

Чтобы увидеть синхронизацию в действии, установить контрольные точки, где указанные в комментариях, запустить его, а затем завершение работы службы (Ctrl-C при работе из командной строки), и посмотреть порядок, что точки останова хита.

0

Вы упомянули об этом, но вы протестировали, если вызывается «void Interrupt()», когда планировщик выключается?

Если это не сработает, я думаю, что нет встроенного способа.

Но если вы хотите изучить все возможности, получите исходный код для Quartz.Net, а вместо ссылки на встроенную библиотеку добавьте все (quartz.net) .csproj в ваш .sln и ссылку «по проекту», ... и выполните этот код таким образом. Или, по крайней мере, посмотрите на исходный код, чтобы получить подсказки.

Тогда гоняться этот

namespace Quartz.Core 
{ 

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

Вот пример IInterruptableJob. Стоит посмотреть, срабатывает ли метод или нет. Возможно, вы уже пробовали это, но поскольку вы не показываете код для задания, это «то, что сделал кто-то другой».

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())); 
     } 
    } 
} 
Смежные вопросы