在系统开发过程中,很多时候我们不能对某一状态或某一行为实时进行人为监控,所以就希望系统能自动实现这一功能。这就需要写一些后台程序,它们可以在应用系统启动时,或者在某个时间点去触发启动,启动后就能自动、定期地在后台运行,这就是后台的定时任务。
定时任务的使用场景有:
在ASP.NET Core中,可以使用托管服务(Hosted Service)结合Timer计时器可以实现后台定时任务。这种方案比较适合简单的场景,对于任务的定时规则设置也很单一,也不用去监控任务的执行状态。本文主要探讨这种方案。
对于更加复杂的应用场景,也可以使用功能更加强大的任务调度框架,来实现定时任务。这样的框架有:
在 ASP.NET Core 中,后台任务是作为托管服务(Hosted Service)实现的。 想要实现后台任务可以实现IHostedService接口或者直接继承BackgroundService抽象类。
开发环境
平台版本是:.NET6
开发框架:ASP.NET Core WebApi
开发工具:Visual Studio 2022
定义一个后台服务类,注意实现IHostedService 接口或直接继承BackgroundService抽象类
/// <summary>
/// 自定义后台任务类
/// </summary>
public class MyHostedService : BackgroundService
{
/// <summary>
/// 执行任务
/// </summary>
/// <param name="stoppingToken"></param>
/// <returns></returns>
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
int i = 1;
//如果没停止,就一直执行
while (!stoppingToken.IsCancellationRequested)
{
DoWork(i++);
}
return Task.CompletedTask;
}
/// <summary>
/// 打印输出一句话
/// </summary>
/// <param name="i"></param>
private void DoWork(int i)
{
Console.WriteLine("后台任务执行MyHostedService,第{0}次", i);
}
}
注册托管服务
//.NET6中的Program.cs中注册后台任务
builder.Services.AddHostedService<MyHostedService>();
运行项目,就会看到后台任务不断被执行
System.Threading.Timer是一个非常常用的定时器类,是一个使用回调方法的计时器,而且由线程池线程服务,简单且对资源要求不高。
//以下演示如何使用Timer类
using System;
using System.Threading;
public class TestTimer
{
/// <summary>
/// 定时器
/// </summary>
private Timer _iTimer;
/// <summary>
/// 构造方法
/// </summary>
public TestTimer()
{
_iTimer = new Timer(Doing,null,TimeSpan.Zero, TimeSpan.FromMinutes(5));
}
/// <summary>
/// 执行具体定定时任务
/// </summary>
/// <param name="nObject"></param>
public void Doing(object nObject)
{
//具体代码。。
}
}
创建后台服务类TimedHostedService,继承BackgroundService抽象基类,该抽象类实现了 IHostedService接口
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
namespace XfTechOAWeb.ApiServer.Background
{
/// <summary>
/// 定时服务
/// </summary>
public class TimedHostedService : BackgroundService
{
/// <summary>
/// 执行任务
/// </summary>
/// <param name="stoppingToken"></param>
/// <returns></returns>
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
return Task.CompletedTask;
}
}
}
实现ExecuteAsync(CancellationToken stoppingToken)抽象方法,启动定时器
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
namespace XfTechOAWeb.ApiServer.Background
{
/// <summary>
/// 定时服务
/// </summary>
public class TimedHostedService : BackgroundService
{
private Timer _timer; //定时器
/// <summary>
/// 执行任务
/// </summary>
/// <param name="stoppingToken"></param>
/// <returns></returns>
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
//定时器创建后马上执行DoWork方法,每10秒钟执行一次
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(10));
return Task.CompletedTask;
}
/// <summary>
/// 定时器具体执行的方法
/// </summary>
/// <param name="state"></param>
private void DoWork(object state)
{
//做一些事情
}
/// <summary>
/// 释放资源
/// </summary>
public override void Dispose()
{
_timer?.Dispose(); //如果_timer对象不为null,则销毁
base.Dispose();
}
}
}
在定时器执行的任务中,使用有作用域的服务,如使用订单服务。
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.DependencyInjection; //IServiceProvider接口的命名空间
using XfTechOAWeb.Application.Interfaces; //应用服务接口的命名空间
namespace XfTechOAWeb.ApiServer.Background
{
/// <summary>
/// 定时服务
/// </summary>
public class TimedHostedService : BackgroundService
{
private readonly IServiceProvider _serviceP;
private Timer _timer; //定时器
/// <summary>
/// 构造方法
/// </summary>
/// <param name="scopeFactory"></param>
public TimedHostedService(IServiceProvider serviceP)
{
_serviceP = serviceP;
}
/// <summary>
/// 执行任务
/// </summary>
/// <param name="stoppingToken"></param>
/// <returns></returns>
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(10*60)); //定时器创建后马上执行,每10分钟执行一次
return Task.CompletedTask;
}
/// <summary>
/// 定时器具体执行的方法
/// </summary>
/// <param name="state"></param>
private void DoWork(object state)
{
//创建服务作用域
using(var scope = _serviceP.CreateScope())
{
//IOrderService 的实现代码 略
var orderService = scope.ServiceProvider.GetRequiredService<IOrderService>();
//检查订单是否已支付,超过10分钟未支付订单自动关闭
orderService.CheckOrderPayment();
}
}
/// <summary>
/// 释放资源
/// </summary>
public override void Dispose()
{
_timer?.Dispose(); //如果_timer对象不为null,则销毁
base.Dispose();
}
}
}
在托管服务中使用服务的一种好方法是在需要时创建作用域。这允许将应用服务/数据库上下文等与设置它们的生命周期配置一起使用。
private readonly IServiceProvider _serviceP;
//创建服务作用域
using(var scope = _serviceP.CreateScope())
{
//...
}
在 IHostBuilder.ConfigureServices
(Program.cs) 中注册托管服务及有作用域的其他服务
builder.Services.AddHostedService<TimedHostedService>(); //注册托管服务--定时任务
builder.Services.AddScoped<IOrderService, OrderService>(); //注册订单服务--有作用域的服务
本文提供了一个简单示例来演示如何使用IHostedService+Timer实现定时任务,并将其注册到ASP.NET Core应用程序中。它的优点是轻量级别,使用简单;如果是比较复杂的,批量的定时任务,并且需要对任务进行监听,还需要并发控制等场景,那么推荐你还是使用第三方定时任务组件,比如QuartZ等主流工具。
如果本文对你有帮助的话,请点赞+关注,或者转发给需要的朋友。