IIS 會自動回收非靜態物件,導致 System.Threading.Timer 在應用程式閒置時被釋放,定時任務中斷。解決關鍵在於將 Timer 參考保持在靜態變數,或改用成熟排程框架 Quartz.net。
錯誤寫法:Timer 為區域變數,方法結束後被 GC 回收。
正確寫法(Global.asax):
public class Global : HttpApplication {
private static Timer _timer; // 靜態變數保持參考
private static Log _log;
protected void Application_Start(object sender, EventArgs e) {
_log = new Log();
_log.Write(ref _timer, 5000); // 每 5 秒執行
}
}
class Log {
public void Write(ref Timer timer, int interval) {
if (timer == null) {
timer = new Timer(DoExecution, this, 0, interval);
}
}
void DoExecution(object state) { /* 定時任務邏輯 */ }
}
Quartz.net 封裝排程複雜度,支援 Cron 運算式、叢集、持久化、監聽器等進階功能。
public class TimeJob : IJob {
public void Execute(IJobExecutionContext context) {
// 定時執行邏輯
}
}
public class Global : HttpApplication {
protected void Application_Start(object sender, EventArgs e) {
// 定義工作
IJobDetail job = JobBuilder.Create<TimeJob>()
.WithIdentity("job1", "group1").Build();
// 定義觸發器:每 5 秒
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.WithSimpleSchedule(x => x.WithIntervalInSeconds(5).RepeatForever())
.Build();
// 啟動排程器
ISchedulerFactory factory = new StdSchedulerFactory();
IScheduler scheduler = factory.GetScheduler();
scheduler.ScheduleJob(job, trigger);
scheduler.Start();
}
}
| 特性 | System.Threading.Timer | Quartz.net |
|---|---|---|
| 排程表達式 | 僅固定間隔 | Cron、Calendar、簡單/複雜觸發 |
| 持久化 | 不支援 | 支援 ADO.NET JobStore |
| 叢集支援 | 無 | 內建叢集鎖機制 |
| 監聽/攔截 | 無 | JobListener、TriggerListener |
| 錯誤處理 | 自行 try-catch | 內建重試、誤火處理 |
生產環境強烈建議採用 Quartz.net 或 Hangfire 等成熟框架。