之前在公司的一个项目中需要用到定时程序,当时使用的是aspnet core提供的IHostedService接口来实现后台定时程序,具体的示例可去官网查看。现在的dotnet core中默认封装了实现IHostedService接口的基类BackgroundService,该类实现如下:
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.Hosting
{
/// <summary>
/// Base class for implementing a long running <see cref="IHostedService"/>.
/// </summary>
public abstract class BackgroundService : IHostedService, IDisposable
{
private Task _executingTask;
private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();
/// <summary>
/// This method is called when the <see cref="IHostedService"/> starts. The implementation should return a task that represents
/// the lifetime of the long running operation(s) being performed.
/// </summary>
/// <param name="stoppingToken">Triggered when <see cref="IHostedService.StopAsync(CancellationToken)"/> is called.</param>
/// <returns>A <see cref="Task"/> that represents the long running operations.</returns>
protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
/// <summary>
/// Triggered when the application host is ready to start the service.
/// </summary>
/// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
public virtual Task StartAsync(CancellationToken cancellationToken)
{
// Store the task we're executing
_executingTask = ExecuteAsync(_stoppingCts.Token);
// If the task is completed then return it, this will bubble cancellation and failure to the caller
if (_executingTask.IsCompleted)
{
return _executingTask;
}
// Otherwise it's running
return Task.CompletedTask;
}
/// <summary>
/// Triggered when the application host is performing a graceful shutdown.
/// </summary>
/// <param name="cancellationToken">Indicates that the shutdown process should no longer be graceful.</param>
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
// Stop called without start
if (_executingTask == null)
{
return;
}
try
{
// Signal cancellation to the executing method
_stoppingCts.Cancel();
}
finally
{
// Wait until the task completes or the stop token triggers
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
}
}
public virtual void Dispose()
{
_stoppingCts.Cancel();
}
}
}
根据BackgroundService源码,我们只要实现该类的抽象方法ExecuteAsync即可。
可以有两种实现方式来做定时程序,第一种就是实现一个Timer:
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace DemoOne.Models
{
public class TimedBackgroundService : BackgroundService
{
private readonly ILogger _logger;
private Timer _timer;
public TimedBackgroundService(ILogger<TimedBackgroundService> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
_logger.LogInformation("周六!");
return Task.CompletedTask;
//Console.WriteLine("MyServiceA is starting.");
//stoppingToken.Register(() => File.Create($"E:\\dotnetCore\\Practice\\Practice\\{DateTime.Now.Millisecond}.txt"));
//while (!stoppingToken.IsCancellationRequested)
//{
// Console.WriteLine("MyServiceA 开始执行");
// await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
// Console.WriteLine("继续执行");
//}
//Console.WriteLine("MyServiceA background task is stopping.");
}
private void DoWork(object state)
{
_logger.LogInformation($"Hello World! - {DateTime.Now}");
}
public override void Dispose()
{
base.Dispose();
_timer?.Dispose();
}
}
}
我们看看StartAsync的源码。上面的实现方式会直接返回一个已完成的Task,这样就会直接运行StartAsync方法的if判断,那么如果我们不走if呢?那么就应该由StartAsync方法返回一个已完成的Task.
第二个即是:
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace DemoOne.Models
{
public class TimedBackgroundService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine("MyServiceA is starting.");
stoppingToken.Register(() => File.Create($"E:\\dotnetCore\\Practice\\Practice\\{DateTime.Now.Millisecond}.txt"));
while (!stoppingToken.IsCancellationRequested)
{
Console.WriteLine("MyServiceA 开始执行");
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
Console.WriteLine("继续执行");
}
Console.WriteLine("MyServiceA background task is stopping.");
}
public override void Dispose()
{
base.Dispose();
}
}
}
最后我们将实现了BackgroundService的类注入到DI即可:
services.AddHostedService<TimedBackgroundService>();
dotnet core的Microsoft.Extensions.Hosting 组件中,充斥着类似IHostedService接口中定义的方法:StartAsync、StopAsync方法。我们注入的HostedService服务会在WebHost类中通过GetRequiredService获取到注入的定时服务。随后执行StartAsync方法开始执行。建议看看Hosting组件源码,会有很多的收获。