在 ASP.NET Core 中,后台任务作为托管服务实现.托管服务是一个类,而且必须实现 IHostedService 接口,该接口定义了两个方法:
- StartAsync(CancellationToken cancellationToken)
该方法包含启动后台任务的逻辑,当启动服务器并触发 IApplicationLifetime.ApplicationStarted
后调用该方法. - StopAsync(CancellationToken
cancellationToken)主机正常关闭时触发,包含结束后台任务和处理任何非托管资源的逻辑.如果应用意外关闭,则可能不会调用.
托管服务在应用启动时激活一次,在应用关闭时正常关闭.实现 IDisposable 时,可在处置服务容器时处理资源.如果在执行后台任务期间引发错误,即使未调用 StopAsync ,也应调用 Dispose.
计时的后台任务
public class TimedHostedService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private Timer _timer;
public TimedHostedService(ILogger<TimedHostedService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is starting." + DateTime.Now);
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));//立即执行一次,每5秒执行一次
return Task.CompletedTask;
}
private void DoWork(object state)
{
_logger.LogInformation("Timed Background Service is working." + DateTime.Now);
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is stopping." + DateTime.Now);
_timer?.Change(Timeout.Infinite, 0);//不再执行
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
复制代码
注册该后台任务:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddHostedService<TimedHostedService>();
}
其实官方帮我们封装了一个类来简化上述代码:
/// <summary>
/// Base class for implementing a long running <see cref="T:Microsoft.Extensions.Hosting.IHostedService" />.
/// </summary>
public abstract class BackgroundService : IHostedService, IDisposable
因此上述代码可以修改成:
public class MyBackGroundTask : BackgroundService
{
private readonly ILogger _logger;
private Timer _timer;
public MyBackGroundTask(ILogger<MyBackGroundTask> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation($" MyBackGroundTask is starting. {DateTime.Now}");
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation($" MyBackGroundTask is working. {DateTime.Now}");
await Task.Delay(5000, stoppingToken);
}
_logger.LogInformation($" MyBackGroundTask is stopping. {DateTime.Now}");
}
}
示例二:在后台任务中使用有作用域的服务
要使用有作用域的服务,需要先创建一个作用域.默认情况下,不会为托管服务创建作用域.
public interface IScopedProcessingService
{
void DoWork();
}
public class ScopedProcessingService : IScopedProcessingService
{
private readonly ILogger _logger;
public ScopedProcessingService(ILogger<ScopedProcessingService> logger)
{
_logger = logger;
}
public void DoWork()
{
_logger.LogInformation($"Scoped Processing Service is working. {DateTime.Now}");
}
}
public class ConsumeScopedServiceHostedService : IHostedService
{
private readonly ILogger _logger;
public IServiceProvider Services { get; }
public ConsumeScopedServiceHostedService(IServiceProvider services, ILogger<ConsumeScopedServiceHostedService> logger)
{
Services = services;
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation($"Consume Scoped Service Hosted Service is starting. {DateTime.Now}");
DoWork();
return Task.CompletedTask;
}
private void DoWork()
{
_logger.LogInformation($"Consume Scoped Service Hosted Service is working. {DateTime.Now}");
using (IServiceScope scope = Services.CreateScope())//创建一个作用域.
{
IScopedProcessingService scopedProcessingService = scope.ServiceProvider.GetRequiredService<IScopedProcessingService>();
scopedProcessingService.DoWork();
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation($"Consume Scoped Service Hosted Service is stopping. {DateTime.Now}");
return Task.CompletedTask;
}
}