Falcon.SugarApi/Falcon.SugarApi/TimedBackgroundTask/TimedTask.cs
2025-02-19 16:45:17 +08:00

140 lines
4.6 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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();
}
}
}