初步TimedTask完成
This commit is contained in:
parent
3805d2b645
commit
4bb1064352
Falcon.SugarApi/TimedBackgroundTask
|
@ -0,0 +1,22 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Falcon.SugarApi.TimedBackgroundTask
|
||||
{
|
||||
/// <summary>
|
||||
/// 服务集合扩展方法
|
||||
/// </summary>
|
||||
public static class IServiceCollectionExtend
|
||||
{
|
||||
/// <summary>
|
||||
/// 向HostService中注册TimedBackgroundTask服务
|
||||
/// </summary>
|
||||
/// <typeparam name="T">TimedBackgroundTask的具体类型</typeparam>
|
||||
/// <param name="services">服务集合</param>
|
||||
/// <returns>服务集合</returns>
|
||||
public static IServiceCollection AddTimedBackgroundTask<T>(this IServiceCollection services)
|
||||
where T : TimedTask {
|
||||
services.AddHostedService<T>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
}
|
135
Falcon.SugarApi/TimedBackgroundTask/TimedTask.cs
Normal file
135
Falcon.SugarApi/TimedBackgroundTask/TimedTask.cs
Normal file
|
@ -0,0 +1,135 @@
|
|||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Falcon.SugarApi.TimedBackgroundTask
|
||||
{
|
||||
/// <summary>
|
||||
/// 由定时器驱动的后台任务
|
||||
/// </summary>
|
||||
public 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 virtual async Task<bool> Run(CancellationToken cancellationToken) => await Task.FromResult(true);
|
||||
|
||||
/// <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);
|
||||
}
|
||||
|
||||
public override void Dispose() {
|
||||
this._timer?.Dispose();
|
||||
base.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user