在業務場景中經常需要后台服務不停的或定時處理一些任務,這些任務是不需要及時響應請求的。
在 asp.net中會使用windows服務來處理。
在 asp.net core中,可以使用托管服務來實現,托管服務是一個類,具有實現IHostService接口的后台任務邏輯。
導入包
使用NUGET添加Microsoft.Extensions.Hosting包到項目中
Microsoft.Extensions.Hosting包地址
IHostedService接口
- 托管服務必須實現IHostedService接口,該接口為主機管理的對象定義了兩種方法。
StartAsync(CancellationToken) - StartAsync包含啟動后台任務的邏輯。使用Web主機時,StartAsync在服務器啟動並且觸發IApplicationLifetime.ApplicationStarted后調用。使用Generic Host時,StartAsync會在ApplicationStarted觸發之前調用。
- StopAsync(CancellationToken) - 在主機執行正常關閉時觸發。StopAsync包含結束后台任務的邏輯。實現IDisposable和終結器(析構函數)來處理任何非托管資源。
取消令牌具有默認的五秒超時,以指示關閉過程不再是正常的。在令牌上請求取消時:
應該中止應用正在執行的任何剩余后台操作。
任何調用的方法都StopAsync應該立即返回。
但是,在請求取消后,任務不會被放棄 - 調用者等待所有任務完成。
如果應用程序被意外關閉(例如,應用程序的進程失敗),則StopAsync可能無法調用。因此,StopAsync是有可能不會被調用的。
托管服務在應用啟動時激活一次,並在應用關閉時正常關閉。為防止異常,最好繼承IDispose接口,釋放資源
定時后台任務用例
可以寫一個托管服務類直接繼承IHostedService,和IDisposable接口
public class MyTimerHostedService : IHostedService, IDisposable
{
private Timer _timer;
public Task StartAsync(CancellationToken cancellationToken)
{
Console.WriteLine("啟動定時任務托管服務");
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromSeconds(0.5));
return Task.CompletedTask;
}
private void DoWork(object state)
{
Console.WriteLine("定時任務處理中");
}
public Task StopAsync(CancellationToken cancellationToken)
{
_timer?.Change(Timeout.Infinite, 0);
Console.WriteLine("停止定時任務");
return Task.CompletedTask;
}
public void Dispose()
{
// 手動釋放定時器
_timer?.Dispose();
}
}
該服務Startup.ConfigureServices使用AddHostedService擴展方法注冊:
services.AddHostedService<MyTimerHostedService>();
使用通用主機啟動 托管服務
class Program
{
static void Main(string[] args)
{
var host = new HostBuilder().ConfigureServices((hostContext, services) =>
{
services.AddHostedService<MyTimerHostedService>();
}).Build();
host.Run();
Console.WriteLine("Hello World!");
Console.ReadLine();
}
}
使用后台服務BackgroundService 實現
.NET Core中實現的抽象BackgroundService基類。
// Copyright (c) .NET Foundation. Licensed under the Apache License, Version 2.0.
/// <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();
protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
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;
}
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();
}
}
由於抽象類已經實現了IHostService接口定義的方法,只需要寫子類去繼承BackgroundService, 在自己的自定義托管服務類中實現ExecuteAsync()方法
public class MyBackGroundService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
Console.WriteLine("MyBackGroundService doing");
//延遲500毫秒執行 相當於使用了定時器
await Task.Delay(500, stoppingToken);
}
}
}
在主機中托管服務
class Program
{
static void Main(string[] args)
{
var host = new HostBuilder().ConfigureServices((hostContext, services) =>
{
//services.AddHostedService<MyTimerHostedService>();
services.AddHostedService<MyBackGroundService>();
}).Build();
host.Run();
Console.WriteLine("Hello World!");
Console.ReadLine();
}
}
使用了HostService后極大的方便了后台任務的管理