140 lines
4.6 KiB
C#
140 lines
4.6 KiB
C#
using Microsoft.Extensions.Hosting;
|
||
using Microsoft.Extensions.Logging;
|
||
using System;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace Falcon.SugarApi.TimedBackgroundTask
|
||
{
|
||
/// <summary>
|
||
/// 由定时器驱动的后台任务
|
||
/// </summary>
|
||
public abstract class TimedTask:BackgroundService, IDisposable
|
||
{
|
||
/// <summary>
|
||
/// 日志记录器
|
||
/// </summary>
|
||
protected readonly ILogger? Logger;
|
||
/// <summary>
|
||
/// 心跳计时器
|
||
/// </summary>
|
||
private readonly PeriodicTimer _timer;
|
||
|
||
/// <summary>
|
||
/// 运行一次任务
|
||
/// </summary>
|
||
/// <param name="cancellationToken">退出信号</param>
|
||
/// <returns>如果继续执行返回True,否则False</returns>
|
||
public abstract Task<bool> Run(CancellationToken cancellationToken);
|
||
|
||
/// <summary>
|
||
/// Timer心跳
|
||
/// </summary>
|
||
public virtual int Heartbeat { get; set; } = 1;
|
||
|
||
/// <summary>
|
||
/// 执行计划的Cron串
|
||
/// </summary>
|
||
public virtual string CronSchedule { get; set; } = "30 * * * * *";
|
||
|
||
/// <summary>
|
||
/// 获取下次执行任务的计划
|
||
/// </summary>
|
||
public CronExpression Schedule { get; private set; }
|
||
|
||
/// <summary>
|
||
/// 任务正在运行
|
||
/// </summary>
|
||
private bool _isRunning = false;
|
||
/// <summary>
|
||
/// 下次执行时间
|
||
/// </summary>
|
||
public DateTime NextTickTime { get; set; } = DateTime.MinValue;
|
||
|
||
/// <summary>
|
||
/// 后台任务开始
|
||
/// </summary>
|
||
protected virtual void OnStart(TimedTask t,CancellationToken stoppingToken) { }
|
||
/// <summary>
|
||
/// 后台任务停止
|
||
/// </summary>
|
||
protected virtual void OnStop(TimedTask t,CancellationToken stoppingToken) { }
|
||
/// <summary>
|
||
/// 完成一次执行
|
||
/// </summary>
|
||
protected virtual void OnCompleted(TimedTask t,CancellationToken stoppingToken) { }
|
||
/// <summary>
|
||
/// 执行中发生未处理异常
|
||
/// </summary>
|
||
protected virtual bool OnException(TimedTask t,Exception ex,CancellationToken stoppingToken) {
|
||
return false;
|
||
}
|
||
/// <summary>
|
||
/// 系统服务
|
||
/// </summary>
|
||
public IServiceProvider Service { get; set; }
|
||
/// <summary>
|
||
/// 资源锁
|
||
/// </summary>
|
||
private static object _lock = new object();
|
||
|
||
/// <summary>
|
||
/// 构造Timed后台任务
|
||
/// </summary>
|
||
/// <param name="service">服务提供器</param>
|
||
public TimedTask(IServiceProvider service) {
|
||
this.Service = service;
|
||
this.Logger = service.GetService(typeof(ILogger<>).MakeGenericType(GetType())) as ILogger;
|
||
_timer = new PeriodicTimer(TimeSpan.FromSeconds(this.Heartbeat));
|
||
this.Schedule = new CronExpression(this.CronSchedule);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 由系统调度执行任务
|
||
/// </summary>
|
||
/// <param name="stoppingToken">退出信号</param>
|
||
/// <returns>无返回</returns>
|
||
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
|
||
this.OnStart(this,stoppingToken);
|
||
while(await _timer.WaitForNextTickAsync(stoppingToken)) {
|
||
lock(_lock) {
|
||
if(stoppingToken.IsCancellationRequested) {
|
||
break;
|
||
}
|
||
if(this._isRunning || DateTime.Now < this.NextTickTime) {
|
||
continue;
|
||
}
|
||
this._isRunning = true;
|
||
}
|
||
try {
|
||
var goOn = await this.Run(stoppingToken);
|
||
this.OnCompleted(this,stoppingToken);
|
||
if(goOn) {
|
||
continue;
|
||
}
|
||
}
|
||
catch(Exception ex) {
|
||
this.OnException(this,ex,stoppingToken);
|
||
}
|
||
SetNextTick();
|
||
this._isRunning = false;
|
||
}
|
||
this.OnStop(this,stoppingToken);
|
||
}
|
||
|
||
private void SetNextTick(string? cronSchedule = null) {
|
||
var s = cronSchedule == null ? this.Schedule : new CronExpression(cronSchedule);
|
||
this.NextTickTime = s.GetNextOccurrence(DateTime.Now);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 释放对象
|
||
/// </summary>
|
||
public override void Dispose() {
|
||
this._timer?.Dispose();
|
||
GC.SuppressFinalize(this);
|
||
base.Dispose();
|
||
}
|
||
}
|
||
}
|