• .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看看效果吧

    写在最后

    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

     

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

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

  • 相关阅读:
    WEB前端网页设计 HTML CSS 网页设计参数 - 【浏览器背景图片】
    如何使用css实现一个加载动画
    计算机行业已经进入寒冬?云计算帮你解决就业难题!
    优化Vue项目架构和模块化:提升应用性能与开发效率
    LoRa模块空中唤醒功能原理和物联网应用
    什么是缓存雪崩、击穿、穿透?
    Android程序设计之学生考勤管理系统
    【论文复现】QuestEval:《QuestEval: Summarization Asks for Fact-based Evaluation》
    python与Electron联合编程记录之九(Electron与Flask联合编程实现)
    【STM32】--基础了解
  • 原文地址:https://www.cnblogs.com/ysmc/p/16468560.html