2

.NET Core 实现后台任务(定时任务)BackgroundService(二) - 一事冇诚

 1 year ago
source link: https://www.cnblogs.com/ysmc/p/16468560.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

原文连接: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看看效果吧

1897432-20220713002203062-236964426.gif

写在最后

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

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

star流程:

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

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

1897432-20220403224815990-1645909476.png

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

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

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

交流群(QQ)欢迎加群讨论

       BA & Blazor ①(795206915)          BA & Blazor ②(675147445)

1897432-20220403225036952-1976037800.png
1897432-20220403225055624-841710730.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK