quartz.net任务不执行的主因是调度器未正确启动:必须await scheduler.start(),且在asp.net core中置于ihostedservice.startasync()或applicationstarted事件,在console应用中需保持主线程存活。

Quartz.NET 任务注册了却从不执行,八成是调度器根本没真正启动——await scheduler.Start() 漏掉、写成同步调用、或放在错误生命周期阶段,都会导致静默失败。
IScheduler.Start() 必须 await,且只能在正确时机调用
Quartz 的 IScheduler 默认处于 STANDBY 状态,不调 await scheduler.Start() 就永远不干活。这不是可选项,是硬性前提。
- ASP.NET Core 中必须放在
IHostedService.StartAsync()或ApplicationStarted事件里;写在ConfigureServices阶段会因 DI 容器未就绪而拿不到依赖,且无任何异常提示 - Console 应用里,
await scheduler.Start()后必须保持主线程存活(比如Console.ReadLine()),否则进程立刻退出,调度器被释放 - 写成
scheduler.Start();(没 await)是高频错误:方法返回Task,但你没等它完成,后续ScheduleJob()可能因调度器未就绪而注册失败或延迟生效
Job 类定义违规导致反射创建失败,全程静默
Quartz 用反射 new 实例,对类结构极其敏感。出错时控制台往往安静如鸡,连日志都不打。
- 类必须是
public,不能是internal、static或嵌套类(nested class) - 必须有
public MyJob()无参构造函数;哪怕只注入ILogger<myjob></myjob>,也不能写带参构造,否则抛JobInstantiationFailedException,提示 “No suitable constructor found” -
Execute方法签名必须是Task Execute(IJobExecutionContext context);如果里面要调异步操作,得包一层await Task.Run(() => { /* 同步逻辑 */ }),直接await会阻塞线程池
Cron 表达式秒级不准?不是表达式写错,是默认策略和线程池扛不住
“0/10 * * * * ?” 看似每 10 秒触发一次,实际容易漏跑或堆积——尤其当 job 执行耗时超过间隔时间。
- 默认
MisfirePolicy是SmartPolicy,对秒级 cron 不友好;应显式指定.WithMisfireHandlingInstructionFireAndProceed() - CronTrigger 本质是按“下次触发时间”计算,不是固定周期轮询;job 耗时 > 间隔时,后续触发可能被跳过,而不是补跑
- 高频任务(≤30 秒)优先用
SimpleTrigger:.WithSimpleSchedule(x => x.WithIntervalInSeconds(10).RepeatForever()),更可控、更少歧义 - 确认线程池大小:
quartz.threadPool.threadCount至少设为 3–5,否则多个 job 并发或单个耗时波动大时,新 trigger 会被丢弃
本地开发别硬上 AdoJobStore,RAMJobStore 才是默认安全起点
刚初始化就报 JobPersistenceException: Failed to obtain DB connection?大概率是本地开发时误配了数据库存储。
- AdoJobStore 需要建表、配连接字符串、处理权限,开发阶段纯属增加障碍;先切回内存存储:
q.UseInMemoryStore()或配置项设为"quartz.jobStore.type = Quartz.Simpl.RAMJobStore, Quartz" - Cron 表达式报
ParseException,90% 是空格问题:“0 0 9 ? * FRI” 合法,“0 0 9?*FRI” 直接炸——Quartz 对空格敏感,不能省 - 部署到 Linux/Docker 时触发不准?大概率是时区没显式指定;加
.InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("China Standard Time")),别依赖系统默认
最常被忽略的点:Quartz 不是 Timer 的平替,它是为复杂调度设计的。每月最后一个周五上午 9 点发邮件?该用。每 5 秒 ping 一次接口?请换 System.Threading.Timer 或 IHostedService —— 否则你调试的不是业务逻辑,是调度器的生命周期和线程池配置。










