WebAPI-定时任务

WebAPI-定时任务

Hosted Services

IHostedService接口

IHostedService 是一个接口,它定义了两个方法:StartAsync(CancellationToken)StopAsync(CancellationToken)。这两个方法分别用于启动和停止后台服务。当你实现这个接口时,你需要自己处理所有的逻辑,包括如何开始、执行任务以及优雅地关闭服务

BackgroundService抽象类

BackgroundService 是一个抽象类,它实现了 IHostedService 接口,并且提供了一个默认的实现来简化开发过程。它引入了一个名为 ExecuteAsync(CancellationToken) 的虚方法,你可以在子类中重写这个方法来实现具体的业务逻辑。BackgroundService 会自动处理一些常见的任务,比如在应用程序关闭时正确地停止服务。

自动触发

BackgroundService 中定义的任务是自动触发的,无需手动触发。当你将一个实现了 IHostedService 接口(或继承自 BackgroundService)的服务注册到 ASP.NET Core 的依赖注入容器中时,ASP.NET Core 会在应用程序启动时自动调用该服务的 StartAsync 方法,并在应用程序关闭时调用 StopAsync 方法

program
1
2
// 注册定时任务类
builder.Services.AddHostedService<MyTask>();
MyTask
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class MyTask: BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// 执行你的任务逻辑
Console.WriteLine("执行定时任务: " + DateTime.Now);

// 等待一段时间再执行下一次
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
}
}

/// <summary>
/// 如果存在需要释放的资源可以填写到这里
/// </summary>
public override void Dispose()
{
// 释放资源
base.Dispose();
}
}

Timer

在 .NET 中,System.Threading.Timer 类提供了一种简单的方式来执行定时任务。它允许你指定一个回调方法,在经过一段指定的时间后调用该方法。Timer 类非常适合用于需要定期或延时执行的任务

1
2
3
4
5
6
public Timer(
TimerCallback callback,
object state,
int dueTime,
int period
)
  • callback:一个 TimerCallback 委托,指向当定时器触发时要调用的方法。
  • state:传递给回调方法的任意对象。这个参数可以用来传递数据到回调方法中。
  • dueTime:定时器启动前的初始延迟时间(以毫秒为单位)。
  • period:定时器触发之间的间隔时间(以毫秒为单位)。如果设置为 -1 或 Timeout.Infinite,则定时器仅触发一次。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class MyTask : BackgroundService
{
private Timer _timer;

protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
// 设置定时器,每30秒触发一次
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(30));

// 返回一个永远不会完成的任务(已经完成的任务),以保持服务运行
// 替代了使用while循环来保持服务运行
return Task.CompletedTask;
}

private void DoWork(object state)
{
// 执行任务逻辑
Console.WriteLine("执行定时任务: " + DateTime.Now);

// 检查是否需要停止
if (stoppingToken.IsCancellationRequested)
{
_timer.Dispose();
}
}

public override void Dispose()
{
// 释放资源
_timer?.Dispose();
base.Dispose();
}
}

Cron表达式

cron表达式是一个字符串,通过crom表达式可以定义任务触发的时间

构成规则:分为6或者个域,由空格分开,每个域代表一个含义

分钟 小时 年(可为空)

注:周和日应该只有一个出现,而为空的时候填写

Cron - 在线Cron表达式生成器


NCrontab

如果想要实现指定规则去执行定时任务的话,仅仅通过Hosted Services是不行的

通过导入第三方库NCrontab,解析cron表达式,通过Timer来配置循环执行的定时任务,再通过Hosted Services来配置好后台任务

注: 因为Dal层是scope注入范围,而定时任务是单例,直接注入会出现异常,使用IServiceScopeFactory获取scope的Dal层对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class MyTask : BackgroundService
{
private readonly CrontabSchedule _schedule;
private Timer? _timer;
private readonly ILogger<MyTask> _logger;

public MyTask(ILogger<MyTask> logger)
{
_logger = logger;
// 解析 CRON 表达式(使得NCrontab支持秒级定时)
_schedule = CrontabSchedule.Parse("5/5 * * * * *", new CrontabSchedule.ParseOptions { IncludingSeconds = true });
}

protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
// 获取下一次执行的时间,并设置定时器
_timer = new Timer(DoWork, null, _schedule.GetNextOccurrence(DateTime.Now) - DateTime.Now, TimeSpan.FromMilliseconds(-1));

return Task.CompletedTask;
}

private void DoWork(object state)
{
// 执行任务
Console.WriteLine("执行每日任务: " + DateTime.Now);

// 重新计算下次执行时间,并重新设置定时器
// 通过不断设置定时器的方式替代Timer的period参数来实现循环
_timer.Change(_schedule.GetNextOccurrence(DateTime.Now) - DateTime.Now, TimeSpan.FromMilliseconds(-1));
}

public override void Dispose()
{
base.Dispose();
_timer?.Dispose();
}
}


WebAPI-定时任务
http://blog.170827.xyz/2024/11/29/WebAPI-定时任务/
作者
XIAOBAI
发布于
2024年11月29日
许可协议