我正在使用Quartz.Net在Windows服务中安排一些长时间运行的作业。我正在尝试优雅地停止服务,例如准备重新启动时。我需要通知作业,以便他们可以根据他们与完成的距离来决定是完成还是中止。我已经看过中断和听众,但我似乎无法弄清楚如何通知挂起的关机。
我也尝试将长期工作分成较小的连续工作,但是当我这样做时,性能会受到严重影响。
答案 0 :(得分:0)
你提到它,但是你在测试程序关闭时调用了“void Interrupt()”吗?
如果这不起作用,我认为没有内置方式。
但是如果你想探索所有可能性,请获取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示例。值得一试,看看方法是否开火。您可能已经尝试过此操作,但由于您没有显示该作业的代码,因此这是“其他人做的样本”。
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()));
}
}
}
答案 1 :(得分: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),并观察断点被命中的顺序。