1.DisallowConcurrentExceution

从字面意思来看也就是不允许并发执行

简单的演示一下

    [DisallowConcurrentExecution]
public class TestDisallowConcurrentExectionJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
await Task.Run(() =>
{ var nextTime = context.NextFireTimeUtc?.ToLocalTime(); var currentTime = DateTime.Now; Console.WriteLine($"CurrentTime={currentTime}, FireTime={context.ScheduledFireTimeUtc?.ToLocalTime()}, NextTime={nextTime}"); }); Thread.Sleep(); }
} public class TestDisallowConcurrentExectionScheduler
{
public async static Task Test()
{
var scheduler = await StdSchedulerFactory.GetDefaultScheduler(); await scheduler.Start(); var job = JobBuilder.CreateForAsync<TestDisallowConcurrentExectionJob>()
.WithIdentity("TestDisallowConcurrentExectionJob")
.UsingJobData("myKey", "myValue")
.Build(); var trigger = TriggerBuilder.Create()
.WithSimpleSchedule(s =>
s.WithIntervalInSeconds()
.RepeatForever())
.Build(); await scheduler.ScheduleJob(job, trigger);
}
}

未添加特性的结果

 添加特性的结果

 Quartz默认是十个线程工作线程加一个调度线程,当线程在等待时,其他工作线程也会进来,而加上DiallowConcurrentExection特性时则都会处于等待状态

原理图

源码解析

通过QuartzSchedulerThread中的Run方法调用AcquireNextTriggers获取下一次的触发器

        public virtual Task<IReadOnlyCollection<IOperableTrigger>> AcquireNextTriggers(
DateTimeOffset noLaterThan,
int maxCount,
TimeSpan timeWindow,
CancellationToken cancellationToken = default)
{
lock (lockObject)
{
var result = new List<IOperableTrigger>(); // return empty list if store has no triggers.
if (timeTriggers.Count == )
{
return Task.FromResult<IReadOnlyCollection<IOperableTrigger>>(result);
} var acquiredJobKeysForNoConcurrentExec = new HashSet<JobKey>();
var excludedTriggers = new HashSet<TriggerWrapper>();
DateTimeOffset batchEnd = noLaterThan; while (true)
{
var tw = timeTriggers.FirstOrDefault();
if (tw == null)
{
break;
}
if (!timeTriggers.Remove(tw))
{
break;
} if (tw.Trigger.GetNextFireTimeUtc() == null)
{
continue;
} if (ApplyMisfire(tw))
{
if (tw.Trigger.GetNextFireTimeUtc() != null)
{
timeTriggers.Add(tw);
}
continue;
} if (tw.Trigger.GetNextFireTimeUtc() > batchEnd)
{
timeTriggers.Add(tw);
break;
} // If trigger's job is set as @DisallowConcurrentExecution, and it has already been added to result, then
// put it back into the timeTriggers set and continue to search for next trigger.
JobKey jobKey = tw.Trigger.JobKey;
IJobDetail job = jobsByKey[jobKey].JobDetail;
if (job.ConcurrentExecutionDisallowed)
{
if (!acquiredJobKeysForNoConcurrentExec.Add(jobKey))
{
excludedTriggers.Add(tw);
continue; // go to next trigger in store.
}
} tw.state = InternalTriggerState.Acquired;
tw.Trigger.FireInstanceId = GetFiredTriggerRecordId();
IOperableTrigger trig = (IOperableTrigger) tw.Trigger.Clone(); if (result.Count == )
{
var now = SystemTime.UtcNow();
var nextFireTime = tw.Trigger.GetNextFireTimeUtc().GetValueOrDefault(DateTimeOffset.MinValue);
var max = now > nextFireTime ? now : nextFireTime; batchEnd = max + timeWindow;
} result.Add(trig); if (result.Count == maxCount)
{
break;
}
} // If we did excluded triggers to prevent ACQUIRE state due to DisallowConcurrentExecution, we need to add them back to store.
if (excludedTriggers.Count > )
{
foreach (var excludedTrigger in excludedTriggers)
{
timeTriggers.Add(excludedTrigger);
}
}
return Task.FromResult<IReadOnlyCollection<IOperableTrigger>>(result);
}
}

RAMJobStore中的TriggersFired方法

当添加了DisallowConcurrentExecution特性后

先从TimeTriggers中移除Trigger

再把Job添加BlockedJobs中

                    if (job.ConcurrentExecutionDisallowed)
{
IEnumerable<TriggerWrapper> trigs = GetTriggerWrappersForJob(job.Key);
foreach (TriggerWrapper ttw in trigs)
{
if (ttw.state == InternalTriggerState.Waiting)
{
ttw.state = InternalTriggerState.Blocked;
}
if (ttw.state == InternalTriggerState.Paused)
{
ttw.state = InternalTriggerState.PausedAndBlocked;
}
timeTriggers.Remove(ttw);
}
blockedJobs.Add(job.Key);
}

RAMJobStore中的TriggeredJobComplete方法

当Job执行完后

如果添加了DisallowConcurrentExecution特性

先移除BlockedJobs中Job 再把Trigger添加到TimeTriggers中

  if (jd.ConcurrentExecutionDisallowed)
{
blockedJobs.Remove(jd.Key);
IEnumerable<TriggerWrapper> trigs = GetTriggerWrappersForJob(jd.Key);
foreach (TriggerWrapper ttw in trigs)
{
if (ttw.state == InternalTriggerState.Blocked)
{
ttw.state = InternalTriggerState.Waiting;
timeTriggers.Add(ttw);
}
if (ttw.state == InternalTriggerState.PausedAndBlocked)
{
ttw.state = InternalTriggerState.Paused;
}
} signaler.SignalSchedulingChange(null, cancellationToken);
}

2.PersistJobDataAfterExecution

从字面意思来看也就是执行后保留作业数据

简单演示一下

 [PersistJobDataAfterExecution]
public class TestPersistJobDataAfterExecutionJob : IJob
{
public async Task Execute(IJobExecutionContext context)
{
await Task.Run(() =>
{ var myVaule = context.JobDetail.JobDataMap["myKey"]; Console.WriteLine(myVaule); context.JobDetail.JobDataMap["myKey"] = myVaule + new Random().Next(,).ToString();
});
}
} public class TestPersistJobDataAfterExcutionScheduler
{
public async static Task Test()
{
var scheduler = await StdSchedulerFactory.GetDefaultScheduler(); await scheduler.Start(); var job = JobBuilder.CreateForAsync<TestPersistJobDataAfterExecutionJob>()
.WithIdentity("TestPersistJobDataAfterExcutionJob")
.UsingJobData("myKey", "myValue")
.Build(); var trigger = TriggerBuilder.Create()
.WithSimpleSchedule(s =>
s.WithIntervalInSeconds()
.RepeatForever())
.Build(); await scheduler.ScheduleJob(job, trigger);
}
}

未加特性的结果

加特性的结果

原理图

源码解析

QuartzSchedulerThread中的Run方法

                                JobRunShell shell;
try
{
shell = qsRsrcs.JobRunShellFactory.CreateJobRunShell(bndle);
await shell.Initialize(qs, CancellationToken.None).ConfigureAwait(false);
}
catch (SchedulerException)
{
await qsRsrcs.JobStore.TriggeredJobComplete(trigger, bndle.JobDetail, SchedulerInstruction.SetAllJobTriggersError, CancellationToken.None).ConfigureAwait(false);
continue;
} var threadPoolRunResult = qsRsrcs.ThreadPool.RunInThread(() => shell.Run(CancellationToken.None));
if (threadPoolRunResult == false)
{
// this case should never happen, as it is indicative of the
// scheduler being shutdown or a bug in the thread pool or
// a thread pool being used concurrently - which the docs
// say not to do...
Log.Error("ThreadPool.RunInThread() returned false!");
await qsRsrcs.JobStore.TriggeredJobComplete(trigger, bndle.JobDetail, SchedulerInstruction.SetAllJobTriggersError, CancellationToken.None).ConfigureAwait(false);
}

JobRunShell初始化方法

 public virtual async Task Initialize(
QuartzScheduler sched,
CancellationToken cancellationToken = default)
{
qs = sched; IJob job;
IJobDetail jobDetail = firedTriggerBundle.JobDetail; try
{
job = sched.JobFactory.NewJob(firedTriggerBundle, scheduler);
}
catch (SchedulerException se)
{
await sched.NotifySchedulerListenersError($"An error occurred instantiating job to be executed. job= '{jobDetail.Key}'", se, cancellationToken).ConfigureAwait(false);
throw;
}
catch (Exception e)
{
SchedulerException se = new SchedulerException($"Problem instantiating type '{jobDetail.JobType.FullName}'", e);
await sched.NotifySchedulerListenersError($"An error occurred instantiating job to be executed. job= '{jobDetail.Key}'", se, cancellationToken).ConfigureAwait(false);
throw se;
} jec = new JobExecutionContextImpl(scheduler, firedTriggerBundle, job);
}

SimpleJobFactory中的NewJob函数可以看出Job是无状态的直接通过反射创建的

    public virtual IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
{
IJobDetail jobDetail = bundle.JobDetail;
Type jobType = jobDetail.JobType;
try
{
if (log.IsDebugEnabled())
{
log.Debug($"Producing instance of Job '{jobDetail.Key}', class={jobType.FullName}");
} return ObjectUtils.InstantiateType<IJob>(jobType);
}
catch (Exception e)
{
SchedulerException se = new SchedulerException($"Problem instantiating class '{jobDetail.JobType.FullName}'", e);
throw se;
}
}

JobRunShell中Run方法将JobExecutionContextImpl塞给了Execute方法

                   private JobExecutionContextImpl jec;

                   // Execute the job
try
{
if (log.IsDebugEnabled())
{
log.Debug("Calling Execute on job " + jobDetail.Key);
} await job.Execute(jec).ConfigureAwait(false); endTime = SystemTime.UtcNow();
}

JobRunShell中Run方法调用的NotifyJobStoreJobComplete函数

await qs.NotifyJobStoreJobComplete(trigger, jobDetail, instCode, cancellationToken).ConfigureAwait(false);

JobRunShell中的NotifyJobStoreJobComplete 可以看出调用了JobStore.TriggeredJobComplete

 public virtual Task NotifyJobStoreJobComplete(
IOperableTrigger trigger,
IJobDetail detail,
SchedulerInstruction instCode,
CancellationToken cancellationToken = default)
{
return resources.JobStore.TriggeredJobComplete(trigger, detail, instCode, cancellationToken);
}

Quartz.NET中StdScheduleFactory如果未指定JobStore则默认RAMJobStore

从而可以看出RAMJobStore中通过TriggerJobComplete方法来检查是否有PersistJobDataAfterExecution特性

如果有通过MemberwiseClone函数克隆出数据来再通过JobBuilder来构建一个JobDetail

                  if (jobDetail.PersistJobDataAfterExecution)
{
JobDataMap newData = jobDetail.JobDataMap;
if (newData != null)
{
newData = (JobDataMap) newData.Clone();
newData.ClearDirtyFlag();
}
jd = jd.GetJobBuilder().SetJobData(newData).Build();
jw.JobDetail = jd;
}
if (jd.ConcurrentExecutionDisallowed)
{
blockedJobs.Remove(jd.Key);
IEnumerable<TriggerWrapper> trigs = GetTriggerWrappersForJob(jd.Key);
foreach (TriggerWrapper ttw in trigs)
{
if (ttw.state == InternalTriggerState.Blocked)
{
ttw.state = InternalTriggerState.Waiting;
timeTriggers.Add(ttw);
}
if (ttw.state == InternalTriggerState.PausedAndBlocked)
{
ttw.state = InternalTriggerState.Paused;
}
} signaler.SignalSchedulingChange(null, cancellationToken);
}

最终会通过JobRunShell中的Run方法中的ReturnJob方法 返回Job

                qs.RemoveInternalSchedulerListener(this);
if (jec != null)
{
if (jec.JobInstance != null)
{
qs.JobFactory.ReturnJob(jec.JobInstance);
} jec.Dispose();
}
        public virtual void ReturnJob(IJob job)
{
var disposable = job as IDisposable;
disposable?.Dispose();
}

Quartz.Net系列(十四):详解Job中两大特性(DisallowConcurrentExecution、PersistJobDataAfterExecution)的更多相关文章

  1. java基础(十四)-----详解匿名内部类——Java高级开发必须懂的

    在这篇博客中你可以了解到匿名内部类的使用.匿名内部类要注意的事项.匿名内部类使用的形参为何要为final. 使用匿名内部类内部类 匿名内部类由于没有名字,所以它的创建方式有点儿奇怪.创建格式如下: n ...

  2. Android笔记(七十四) 详解Intent

    我们最常使用Intent来实现Activity之间的转跳,最近做一个app用到从系统搜索图片的功能,使用到了intent的 setType 方法和 setAction 方法,网上搜索一番,发现实现转跳 ...

  3. 广告行业中那些趣事系列8:详解BERT中分类器源码

    最新最全的文章请关注我的微信公众号:数据拾光者. 摘要:BERT是近几年NLP领域中具有里程碑意义的存在.因为效果好和应用范围广所以被广泛应用于科学研究和工程项目中.广告系列中前几篇文章有从理论的方面 ...

  4. 爬虫系列 | 6、详解爬虫中BeautifulSoup4的用法

    bs4,全称BeautifulSoup 4 , 它是Python独有的一种解析方式.也就是说只有Python语言才可以通过这种方式去解析数据. BeautifulSoup 3 只支持Python2,所 ...

  5. 详解Redis中两种持久化机制RDB和AOF(面试常问,工作常用)

    redis是一个内存数据库,数据保存在内存中,但是我们都知道内存的数据变化是很快的,也容易发生丢失.幸好Redis还为我们提供了持久化的机制,分别是RDB(Redis DataBase)和AOF(Ap ...

  6. 详解Redis中两种持久化机制RDB和AOF

    redis是一个内存数据库,数据保存在内存中,但是我们都知道内存的数据变化是很快的,也容易发生丢失.幸好Redis还为我们提供了持久化的机制,分别是RDB(Redis DataBase)和AOF(Ap ...

  7. “全栈2019”Java第八十四章:接口中嵌套接口详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  8. Hexo系列(二) 配置文件详解

    Hexo 是一款优秀的博客框架,在使用 Hexo 搭建一个属于自己的博客网站后,我们还需要对其进行配置,使得 Hexo 更能满足自己的需求 这里所说的配置文件,是位于站点根目录下的 _config.y ...

  9. 十图详解tensorflow数据读取机制(附代码)转知乎

    十图详解tensorflow数据读取机制(附代码) - 何之源的文章 - 知乎 https://zhuanlan.zhihu.com/p/27238630

随机推荐

  1. Codeforces Round #561 (Div. 2) A Tale of Two Lands 【二分】

    A Tale of Two Lands 题目链接(点击) The legend of the foundation of Vectorland talks of two integers xx and ...

  2. iOS简历书写注意事项

    1.个人信息模块 1)简历标题 2)姓名 性别  年龄 电话  邮箱  常驻地 学历 英语能力 工作年限 籍贯 专业 (突出优势) 注意:不要从招聘网站导出简历网站 2.求职意向 1)职位 地点 薪资 ...

  3. Calculating a “Row X of Y”

    显示 “Row X of Y,” ,X是当前行,Y是总行数,  那就是 ROW_NUMBER(ORDER BY stor_id) of Count(*) OVER()此处还是以样例数据库 pub 为例 ...

  4. 从零开始的Spring Boot(1、搭建一个Spring Boot项目Hello World)

    搭建一个Spring Boot项目Hello World 写在前面 从零开始的Spring Boot(2.在Spring Boot中整合Servlet.Filter.Listener的方式):http ...

  5. openresty用haproxy2.0实现负载均衡

    安装openresty 编译安装 yum install pcre-devel openssl-devel gcc curl wget wget https://openresty.org/downl ...

  6. Web前端 -- Webpack

    一.Webpack Webpack 是一个前端资源加载/打包工具.它将根据模块的依赖关系进行静态分析,然后将这些模块按照指定的规则生成对应的静态资源. 二.Webpack安装 1.全局安装 npm i ...

  7. Linux 开放指定端口号

    1.开启防火墙 [root@localhost opt]# systemctl start firewalld 2.查看当前防火墙状态 [root@localhost opt]# systemctl ...

  8. Blazor带我重玩前端(一)

    写在前面 曾经我和前端朋友聊天的时候,我说我希望有一天可以用C#写前端,不过当时更多的是美好的想象,而现在这一切正变得真实…… 什么是Blazor 我们知道浏览器可以正确解释并执行JavaScript ...

  9. hashcode和==

    public class Main { public static void main(String[] args) { Object o=new Object(); System.out.print ...

  10. c语音学习笔记

    1.学习教程参考了杨光福 android jni Android视频<JNI> http://edu.csdn.net/course/detail/3235/54186?auto_star ...