首页 > 其他分享 >BackgroundService

BackgroundService

时间:2022-11-01 09:11:18浏览次数:72  
标签:CancellationToken Task BackgroundService StartAsync IHostedService StopAsync

.NET Core 实现后台任务(定时任务)BackgroundService

.NET Core 实现后台任务(定时任务)IHostedService

 

原文链接:https://www.cnblogs.com/ysmc/p/16456787.html

  最近有小伙伴问道,在 .Net Core 中,如何定时执行任务,而因为需要执行的任务比较简单,并不想使用其它的中间件(如 Quartz 等等),在这样的需求下,我给大家介绍一下.Net 内置的后台任务 IHostedService。

  贯彻本人的习惯,不废话,直接上代码,首先我们创建一个类,该类继承 IHostedService 接口,并实现该接口成员,同时我用到了 .NET内置的定时器类 Timer,具体这个 Timer 是什么,我这里就不介绍了,有兴趣的小伙伴可以到官方文档了解 传送门

  在不需要定时执行任务的时候,也可以在这里进行应用启动后的操作,例如创建 RabbitMQ 连接【手动狗头】

  同时,我们需要在 Program.cs(.Net 5 及一下在 Startup.cs)中添加以下配置。

// .Net 6
builder.Services.AddHostedService<TestHostedService>();

// .Net  5 及以下
services.AddHostedService<TestHostedService>();
复制代码
 1 public class TestHostedService : IHostedService, IDisposable
 2 {
 3     private Timer? _timer;
 4 
 5     public Task StartAsync(CancellationToken cancellationToken)
 6     {
 7         _timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromSeconds(5));
 8 
 9         return Task.CompletedTask;
10     }
11 
12     private void DoWork(object? state)
13     {
14         Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss}");
15     }
16 
17     public Task StopAsync(CancellationToken cancellationToken)
18     {
19         Console.WriteLine("StopAsync");
20 
21         return Task.CompletedTask;
22     }
23 
24 
25     public void Dispose()
26     {
27         _timer?.Dispose();
28     }        
29 }
复制代码

  上面的代码非常简单,应用在运行后,会去执行 StartAsync 函数,应用关闭执行 StopAsync,由于我在这里使用的定时器,所以每过5秒都会执行一次 DoWork 函数,以下是运行效果

 

   需要注意的是,这里的定时是不等待任务执行完成,只要时间一到,就会调用 DoWork 函数,所以适合一些简单、特定的场景,后续我将针对这些问题多写几个文档,大佬们有什么建议尽管提,加油!

以下为官方文档对 IHostedService 接口 的说明


 

IHostedService 接口

IHostedService 接口为主机托管的对象定义了两种方法:

  • StartAsync(CancellationToken)
  • StopAsync(CancellationToken)

StartAsync

StartAsync(CancellationToken) 包含用于启动后台任务的逻辑。 在以下操作之前调用 StartAsync

  • 已配置应用的请求处理管道。
  • 已启动服务器且已触发 IApplicationLifetime.ApplicationStarted。

StartAsync 应仅限于短期任务,因为托管服务是按顺序运行的,在 StartAsync 运行完成之前不会启动其他服务。

StopAsync

  • StopAsync(CancellationToken) 在主机执行正常关闭时触发。 StopAsync 包含结束后台任务的逻辑。 实现 IDisposable 和终结器(析构函数)以处置任何非托管资源。

默认情况下,取消令牌会有五秒超时,以指示关闭进程不再正常。 在令牌上请求取消时:

  • 应中止应用正在执行的任何剩余后台操作。
  • StopAsync 中调用的任何方法都应及时返回。

但是,在请求取消后,将不会放弃任务,调用方会等待所有任务完成。

如果应用意外关闭(例如,应用的进程失败),则可能不会调用 StopAsync。 因此,在 StopAsync 中执行的任何方法或操作都可能不会发生。

若要延长默认值为 5 秒的关闭超时值,请设置:

  • ShutdownTimeout(当使用通用主机时)。
  • 使用 Web 主机时为关闭超时值主机配置设置。

托管服务在应用启动时激活一次,在应用关闭时正常关闭。 如果在执行后台任务期间引发错误,即使未调用 StopAsync,也应调用 Dispose

 

原文连接:https://www.cnblogs.com/ysmc/p/16468560.html

  在上一篇文档中说到使用 IHostedService 接口实现定时任务  传送门,其中,有小伙伴就问到,为什么不使用 BackgroundService,我个人觉得使用什么技术,应该取决于需求,代码只是一种工具,用得顺手对于编码人员来说,我个人感觉还是非常重要的;正好也说到了 BackgroundService,那这一篇文档就简单说一下它吧。

  首先我们看一下官方的说明,学习代码一定要看官方的文档,尽管有时候会有点晦涩难懂,但肯定是最正确的:


BackgroundService 基类

BackgroundService 是用于实现长时间运行的 IHostedService 的基类。

调用 ExecuteAsync(CancellationToken) 来运行后台服务。 实现返回一个 Task,其表示后台服务的整个生存期。

在 ExecuteAsync 变为异步(例如通过调用 await)之前,不会启动任何其他服务。 避免在 ExecuteAsync 中执行长时间的阻塞初始化工作。 

StopAsync(CancellationToken) 中的主机块等待完成 ExecuteAsync

调用 IHostedService.StopAsync 时,将触发取消令牌。 当激发取消令牌以便正常关闭服务时,ExecuteAsync 的实现应立即完成。 否则,服务将在关闭超时后不正常关闭。

StartAsync 应仅限于短期任务,因为托管服务是按顺序运行的,在 StartAsync 运行完成之前不会启动其他服务。 长期任务应放置在 ExecuteAsync 中。


  针对第一点“BackgroundService 是用于实现长时间运行的 IHostedService 的基类”,我们先看看 BackgroundService 的源码:

复制代码
 1 public abstract class BackgroundService : IHostedService, IDisposable
 2 {
 3     private Task _executingTask;
 4     private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();
 5 
 6     /// <summary>
 7     /// This method is called when the <see cref="IHostedService"/> starts. The implementation should return a task that represents
 8     /// the lifetime of the long running operation(s) being performed.
 9     /// /// </summary>
10     /// <param name="stoppingToken">Triggered when <see cref="IHostedService.StopAsync(CancellationToken)"/> is called.</param>
11     /// <returns>A <see cref="Task"/> that represents the long running operations.</returns>
12     protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
13 
14     /// <summary>
15     /// Triggered when the application host is ready to start the service.
16     /// </summary>
17     /// <param name="cancellationToken">Indicates that the start process has been aborted.</param>
18     public virtual Task StartAsync(CancellationToken cancellationToken)
19     {
20         // Store the task we're executing
21         _executingTask = ExecuteAsync(_stoppingCts.Token);
22 
23         // If the task is completed then return it, this will bubble cancellation and failure to the caller
24         if (_executingTask.IsCompleted)
25         {
26             return _executingTask;
27         }
28 
29         // Otherwise it's running
30         return Task.CompletedTask;
31     }
32 
33     /// <summary>
34     /// Triggered when the application host is performing a graceful shutdown.
35     /// </summary>
36     /// <param name="cancellationToken">Indicates that the shutdown process should no longer be graceful.</param>
37     public virtual async Task StopAsync(CancellationToken cancellationToken)
38     {
39         // Stop called without start
40         if (_executingTask == null)
41         {
42             return;
43         }
44 
45         try
46         {
47             // Signal cancellation to the executing method
48             _stoppingCts.Cancel();
49         }
50         finally
51         {
52             // Wait until the task completes or the stop token triggers
53             await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
54         }
55 
56     }
57 
58     public virtual void Dispose()
59     {
60         _stoppingCts.Cancel();
61     }
62 }
复制代码

  以上代码很好的解答了小伙伴提出“为什么不使用 BackgroundService”的问题,在上一篇文章中,评论区的一位大佬也很好的回答了这位小伙伴的问题,我这里引用下这位大佬的原话:“BackgroundService 是 IHostedService的一个简单实现,内部IHostedService 的StartAsync调用了ExecuteAsync”,本质上就是使用了 IHostedService;

  让我们回到正题,怎么用 BackgroundService 实现定时任务呢,老规矩,上代码:

首先,创建一个服务接口,定义需要实现的任务,以及对应的实现,如果需要执行异步方法,记得加上 await,不然任务将不会等待执行结果,直接进行下一个任务。

复制代码
 1 public class TaskWorkService : ITaskWorkService
 2 {
 3     public async Task TaskWorkAsync(CancellationToken stoppingToken)
 4     {
 5         while (!stoppingToken.IsCancellationRequested)
 6         {
 7             //执行任务
 8             Console.WriteLine($"{DateTime.Now}");
 9 
10             //周期性任务,于上次任务执行完成后,等待5秒,执行下一次任务
11             await Task.Delay(500);
12         }
13     }
14 }
复制代码

  注册服务

builder.Services.AddScoped<ITaskWorkService, TaskWorkService>();

  创建后台服务类,继承基类 BackgroundService,这里需要注意的是,要在 BackgroundService 中使用有作用域的服务,请创建作用域, 默认情况下,不会为托管服务创建作用域,得自己管理服务的生命周期,切记!于构造函数中注入 IServiceProvider即可。

复制代码
 1 public class BackgroundServiceDemo : BackgroundService
 2 {
 3     private readonly IServiceProvider _services;
 4 
 5     public BackgroundServiceDemo(IServiceProvider services)
 6     {
 7         _services = services;
 8     }
 9 
10     protected override async Task ExecuteAsync(CancellationToken stoppingToken)
11     {
12         using var scope = _services.CreateScope();
13 
14         var taskWorkService = scope.ServiceProvider.GetRequiredService<ITaskWorkService>();
15 
16         await taskWorkService.TaskWorkAsync(stoppingToken);
17     }
18 }
复制代码

  最后别忘了这个类也是需要注册的,注册方式与 IHostedService 接口的方式一样

builder.Services.AddHostedService<BackgroundServiceDemo>();

  大功告成,F5看看效果吧

写在最后

Bootstrap Blazor 官网地址:https://www.blazor.zone

  希望大佬们看到这篇文章,能给项目点个star支持下,感谢各位!

star流程:

1、访问点击项目链接:BootstrapBlazor   star

2、点击star,如下图,即可完成star,关注项目不迷路:

 

另外还有两个GVP项目,大佬们方便的话也点下star呗,非常感谢:

  BootstrapAdmin 项目地址:star
  https://gitee.com/LongbowEnterprise/BootstrapAdmin

  SliderCaptcha 项目地址:star
  https://gitee.com/LongbowEnterprise/SliderCaptcha

标签:CancellationToken,Task,BackgroundService,StartAsync,IHostedService,StopAsync
From: https://www.cnblogs.com/Leo_wl/p/16846586.html

相关文章