.NET Core实现定时后台任务的几种方法。
前言
在开发应用时,定时任务是一个常见的需求。它可以自动化周期性的操作,例如定期同步数据、定时清理缓存、发送通知等。
应用场景
- 数据处理
- 数据同步:定时从外部数据源(如数据库、API等)同步数据到本地系统
- 数据备份:定期备份数据库或文件系统中的数据,以防止数据丢失
- 系统维护
- 缓存更新:定时刷新缓存数据,确保缓存中的数据是最新的
- 日志清理:定时清理日志文件,防止日志文件占用过多磁盘空间
- 任务调度
- 定时任务调度:根据预设的时间表或条件,自动执行特定的任务。例如,每天凌晨自动执行数据备份任务
- 任务队列处理:定时从任务队列中取出任务并执行,适用于需要异步处理的场景
- 用户交互
- 定时通知:定时向用户发送通知或提醒,例如发送邮件、短信或应用内通知
实现方式
基于IHostedService接口实现自定义后台服务
定时后台任务使用System.Threading.Timer类。在StartAsync上使用计时器执行DoWork任务,在StopAsync上禁用计时器,并在Dispose上处置服务容器时处置计时器。
Timer不等待先前的DoWork执行完成。使用Interlocked.Increment以原子操作的形式将执行计数器递增,这可确保多个线程不会并行更新executionCount。
具体实现
首先创建自定义的后台服务类,
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 38 39 40 41 42 43
| public class TimedHostedService : IHostedService, IDisposable { private int executionCount = 0; private readonly ILogger<TimedHostedService> _logger; private Timer _timer;
public TimedHostedService(ILogger<TimedHostedService> logger) { _logger = logger; }
public Task StartAsync(CancellationToken stoppingToken) { _logger.LogInformation("Timed Hosted Service running.");
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
return Task.CompletedTask; }
private void DoWork(object state) { var count = Interlocked.Increment(ref executionCount);
_logger.LogInformation( "Timed Hosted Service is working. Count: {Count}", count); }
public Task StopAsync(CancellationToken stoppingToken) { _logger.LogInformation("Timed Hosted Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask; }
public void Dispose() { _timer?.Dispose(); } }
|
然后在Startup中注册服务。
1 2 3 4
| public void ConfigureServices(IServiceCollection services) { services.AddHostedService<TimedHostedService>(); }
|
使用BackgroundService
BackgroundService是用于实现长时间运行的IHostedService的基类,是IHostedService的一个简单实现。
StartAsync应仅限于短期任务,因为托管服务是按顺序运行的,在StartAsync运行完成之前不会启动其他服务。长期任务应放置在ExecuteAsync中。
具体实现
首先创建服务类,
1 2 3 4
| public interface IWorkService { Task TaskWorkAsync(CancellationToken cancellationToken); }
|
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 38 39 40 41 42 43
| public class WorkService : IWorkService { private int executionCount = 0; private readonly ILogger<WorkService> _logger;
public WorkService(ILogger<WorkService> logger) { _logger = logger; }
public async Task TaskWorkAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { var count = Interlocked.Increment(ref executionCount); _logger.LogInformation("Timed Hosted Service is working. Count: {Count}", count);
var StartHour = 0; var StartMinute = 0; var StartSecond = 0; var IntervalMinute = 1440;
var now = DateTime.Now; var nextDateTime = new DateTime(now.Year, now.Month, now.Day, StartHour, StartMinute, StartSecond).AddMinutes(IntervalMinute); if(nextDateTime < now) { var delay = nextDateTime.AddDays(1) - now; await Task.Delay(delay, cancellationToken); } else { var delay = nextDateTime - now; await Task.Delay(delay, cancellationToken); await DoWork(); }
} }
private async Task DoWork(){} }
|
然后创建后台服务调用类,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class CustomBackgroundService : BackgroundService { private readonly IServiceProvider _services;
public CustomBackgroundService(IServiceProvider services) { _services = services; }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { using var scope = _services.CreateScope(); var taskWorkService = scope.ServiceProvider.GetRequiredService<IWorkService>(); await taskWorkService.TaskWorkAsync(stoppingToken); } }
|
最后在Startup中注册后台服务,并添加主机服务。
1 2 3 4 5
| public void ConfigureServices(IServiceCollection services) { services.AddScoped<IWorkService, WorkService>(); services.AddHostedService<CustomBackgroundService>(); }
|
参考文档