开局一张图,故事慢慢编!这是一个后台任务打印时间的德莫,代码如下:

  1. using BackGroundTask;
  2.  
  3. var builder = WebApplication.CreateBuilder();
  4. builder.Services.AddTransient<TickerService>();
  5. builder.Services.AddHostedService<TickerBackGroundService>();
  6. builder.Build().Run();
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6.  
  7. namespace BackGroundTask
  8. {
  9. internal class TickerService
  10. {
  11. private event EventHandler<TickerEventArgs> Ticked;
  12. public TickerService()
  13. {
  14. Ticked += OnEverySecond;
  15. Ticked += OnEveryFiveSecond;
  16. }
  17. public void OnEverySecond(object? sender,TickerEventArgs args)
  18. {
  19. Console.WriteLine(args.Time.ToLongTimeString());
  20. }
  21. public void OnEveryFiveSecond(object? sender, TickerEventArgs args)
  22. {
  23. if(args.Time.Second %5==0)
  24. Console.WriteLine(args.Time.ToLongTimeString());
  25. }
  26. public void OnTick(TimeOnly time)
  27. {
  28. Ticked?.Invoke(this, new TickerEventArgs(time));
  29. }
  30. }
  31. internal class TickerEventArgs
  32. {
  33. public TimeOnly Time { get; }
  34. public TickerEventArgs(TimeOnly time)
  35. {
  36. Time = time;
  37. }
  38. }
  39. }
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6.  
  7. namespace BackGroundTask
  8. {
  9. internal class TickerBackGroundService : BackgroundService
  10. {
  11. private readonly TickerService _tickerService;
  12. public TickerBackGroundService(TickerService tickerService)
  13. {
  14. _tickerService = tickerService;
  15. }
  16. protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  17. {
  18. while (!stoppingToken.IsCancellationRequested)
  19. {
  20. _tickerService.OnTick(TimeOnly.FromDateTime(DateTime.Now));
  21. await Task.Delay(1000,stoppingToken);
  22. }
  23. }
  24. }
  25. }

结果和预期一样,每秒打印一下时间,五秒的时候会重复一次。

代码微调,把打印事件改成打印guid,新增TransientService类:

  1. internal class TransientService
  2. {
  3. public Guid Id { get; }=Guid.NewGuid();
  4. }

微调后代码如下:

  1. using BackGroundTask;
  2.  
  3. var builder = WebApplication.CreateBuilder();
  4. builder.Services.AddTransient<TickerService>();
  5. builder.Services.AddTransient<TransientService>(); //新增生成guid类
  6. builder.Services.AddHostedService<TickerBackGroundService>();
  7. builder.Build().Run();
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6.  
  7. namespace BackGroundTask
  8. {
  9. internal class TickerService
  10. {
  11. private event EventHandler<TickerEventArgs> Ticked;
  12. private readonly TransientService _transientService; //注入TransientService
  13. public TickerService(TransientService transientService)
  14. {
  15. Ticked += OnEverySecond;
  16. Ticked += OnEveryFiveSecond;
  17. _transientService = transientService;
  18.  
  19. }
  20. public void OnEverySecond(object? sender,TickerEventArgs args)
  21. {
  22. Console.WriteLine(_transientService.Id); //打印guid
  23. }
  24. public void OnEveryFiveSecond(object? sender, TickerEventArgs args)
  25. {
  26. if(args.Time.Second %5==0)
  27. Console.WriteLine(args.Time.ToLongTimeString());
  28. }
  29. public void OnTick(TimeOnly time)
  30. {
  31. Ticked?.Invoke(this, new TickerEventArgs(time));
  32. }
  33. }
  34. internal class TickerEventArgs
  35. {
  36. public TimeOnly Time { get; }
  37. public TickerEventArgs(TimeOnly time)
  38. {
  39. Time = time;
  40. }
  41. }
  42. }

TickerBackGroundService类没有做改动,来看看结果:

看似没问题,但是这个guid每次拿到的是一样的,再来看注入的TransientService类,是瞬时的,而且TickerService也是瞬时的。那应该每次会拿到新的对象新的guid才对。那这个后台任务是不是满足不了生命周期控制的要求呢?

问题就出在下面的代码上:

  1.        while (!stoppingToken.IsCancellationRequested)
  2. {
  3. _tickerService.OnTick(TimeOnly.FromDateTime(DateTime.Now));
  4. await Task.Delay(1000,stoppingToken);
  5. }

任务只要不停止,循环会一直下去,所以构造函数注入的类不会被释放,除非程序重启。那么怎么解决这个问题呢,那就是在while里面每次每次循环都创建一个新的对象。那就可以引入ServiceProvider对象。改造后的代码如下:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6.  
  7. namespace ConsoleBackGround
  8. {
  9. internal class GlobalService
  10. {
  11. public static IServiceProvider ServiceProvider { get; set; }
  12. }
  13. }
  1. using ConsoleBackGround;
  2.  
  3. var builder = WebApplication.CreateBuilder();
  4.  
  5. builder.Services.AddTransient<TransientService>(); //Guid相同
  6. //builder.Services.AddSingleton<TransientService>(); //构造函数使用Guid相同,使用scope对象注入不了,必须用ATransient
  7. //builder.Services.AddScoped<TransientService>(); //构造函数使用Guid相同, 使用scope对象注入不了,必须用ATransient
  8. builder.Services.AddTransient<TickerService>();
  9.  
  10. GlobalService.ServiceProvider = builder.Services.BuildServiceProvider(); //一定要在注入之后赋值,要不然只会拿到空对象。
  11. builder.Services.AddHostedService<TickerBackGroundService>();
  12.  
  13. builder.Build().Run();
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6.  
  7. namespace ConsoleBackGround
  8. {
  9. internal class TickerBackGroundService : BackgroundService
  10. {
  11. //private readonly TickerService _tickerService;
  12. //public TickerBackGroundService(TickerService tickerService)
  13. //{
  14. // _tickerService = tickerService;
  15. //}
  16. protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  17. {
  18. while (!stoppingToken.IsCancellationRequested)
  19. {
  20. //_tickerService.OnTick(TimeOnly.FromDateTime(DateTime.Now)); //guid不会变
  21. using var scope = GlobalService.ServiceProvider.CreateScope();
  22. var _tickerService = scope.ServiceProvider.GetService<TickerService>();
  23. _tickerService?.OnTick(TimeOnly.FromDateTime(DateTime.Now)); //可以保证guid会变
  24. await Task.Delay(1000,stoppingToken);
  25. }
  26. }
  27. }
  28. }

问题出在循环上所以TickerService代码不需要做任何更改。针对方便构造函数注入serviceprovider的情况完全不需要全局的GlobalService,通过构造函数注入的代码如下:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6.  
  7. namespace ConsoleBackGround
  8. {
  9. internal class TickerBackGroundService : BackgroundService
  10. {
  11. private readonly IServiceProvider _sp;
  12. public TickerBackGroundService(IServiceProvider sp)
  13. {
  14. _sp = sp;
  15. }
  16. protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  17. {
  18. while (!stoppingToken.IsCancellationRequested)
  19. {
  20. ////_tickerService.OnTick(TimeOnly.FromDateTime(DateTime.Now)); //guid不会变
  21. using var scope = _sp.CreateScope();
  22. var _tickerService = scope.ServiceProvider.GetService<TickerService>();
  23. _tickerService?.OnTick(TimeOnly.FromDateTime(DateTime.Now)); //可以保证guid会变
  24. await Task.Delay(1000,stoppingToken);
  25. }
  26. }
  27. }
  28. }

运行结果符合预期:

下面看看使用MediatR的代码,也可以达到预期:

  1. using BackGroundMediatR;
  2. using MediatR;
  3.  
  4. Console.Title = "BackGroundMediatR";
  5. var builder = WebApplication.CreateBuilder();
  6. //builder.Services.AddSingleton<TransientService>(); //打印相同的guid
  7. builder.Services.AddTransient<TransientService>(); //打印不同的guid
  8. builder.Services.AddMediatR(typeof(Program));
  9.  
  10. builder.Services.AddHostedService<TickerBackGroundService>();
  11.  
  12. builder.Build().Run();
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6.  
  7. namespace BackGroundMediatR
  8. {
  9. internal class TransientService
  10. {
  11. public Guid Id { get; }=Guid.NewGuid();
  12. }
  13. }
  1. using MediatR;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7.  
  8. namespace BackGroundMediatR
  9. {
  10. internal class TimedNotification:INotification
  11. {
  12. public TimeOnly Time { get; set; }
  13. public TimedNotification(TimeOnly time)
  14. {
  15. Time = time;
  16. }
  17. }
  18. }
  1. using MediatR;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7.  
  8. namespace BackGroundMediatR
  9. {
  10. internal class EventSecondHandler : INotificationHandler<TimedNotification>
  11. {
  12. private readonly TransientService _service;
  13. public EventSecondHandler(TransientService service)
  14. {
  15. _service = service;
  16. }
  17. public Task Handle(TimedNotification notification, CancellationToken cancellationToken)
  18. {
  19. Console.WriteLine(_service.Id);
  20. return Task.CompletedTask;
  21. }
  22. }
  23. }
  1. using MediatR;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7.  
  8. namespace BackGroundMediatR
  9. {
  10. internal class EveryFiveSecondHandler : INotificationHandler<TimedNotification>
  11. {
  12. public Task Handle(TimedNotification notification, CancellationToken cancellationToken)
  13. {
  14. if(notification.Time.Second % 5==0)
  15. Console.WriteLine(notification.Time.ToLongTimeString());
  16. return Task.CompletedTask;
  17. }
  18. }
  19. }
  1. using MediatR;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7.  
  8. namespace BackGroundMediatR
  9. {
  10. internal class TickerBackGroundService : BackgroundService
  11. {
  12. private readonly IMediator _mediator;
  13. public TickerBackGroundService(IMediator mediator)
  14. {
  15. _mediator = mediator;
  16. }
  17. protected override async Task ExecuteAsync(CancellationToken stoppingToken)
  18. {
  19. while (!stoppingToken.IsCancellationRequested)
  20. {
  21. var timeNow = TimeOnly.FromDateTime(DateTime.Now);
  22. await _mediator.Publish(new TimedNotification(timeNow));
  23. await Task.Delay(1000,stoppingToken);
  24. }
  25. }
  26. }
  27. }

执行结果如下:

代码链接:

exercise/Learn_Event at master · liuzhixin405/exercise (github.com)

Over!

netcore后台任务注意事项的更多相关文章

  1. 【5min+】后台任务的积木。.NetCore中的IHostedService

    系列介绍 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net ...

  2. .netcore开发环境和服务器注意事项

    对于开发环境,如果你需要使用.netcore命令的话,你需要安装SDK:如果你还需要运行.netcore的网站的话,你必须还要安装它的[runtime]和[hosting server]: 对于服务器 ...

  3. Swagger/OpenAPI By Swashbuckle在NetCore 3.1中较NetCore 2.2使用的注意事项及入门

    方案选择 使用Web API时,了解其各种方法对开发人员来说可能是一项挑战. Swagger也称为OpenAPI(Open Application Programming Interface,开放应用 ...

  4. 在.netcore webapi项目中使用后台任务工具Hangfire

    安装Hangfire 在webapi项目中通过nuget安装Hangfire.Core,Hangfire.SqlServer,Hangfire.AspNetCore,截止到目前的最新版本是1.7.6. ...

  5. 开发Adobe AIR移动应用程序的考虑事项

    http://www.adobe.com/cn/devnet/air/articles/considerations-air-apps-mobile.html Adobe AIR 经过发展演进,已经超 ...

  6. 与众不同 windows phone (13) - Background Task(后台任务)之后台文件传输(上传和下载)

    原文:与众不同 windows phone (13) - Background Task(后台任务)之后台文件传输(上传和下载) [索引页][源码下载] 与众不同 windows phone (13) ...

  7. ubuntu16.04-x64系统中Jexus web server部署.NetCore和端口分析引发的猜想!

    您有这样的牢骚么? 有一周没更新博客了,简单说下在干什么吧:主要是公司安排对接某旅游大公司的接口,接口数量倒也就10个左右,对接完后还需要加入到业务系统中和App端,因此还是需要花点时间的:时间上来说 ...

  8. .NetCore+Jexus代理+Redis模拟秒杀商品活动

    开篇叙 本篇将和大家分享一下秒杀商品活动架构,采用的架构方案正如标题名称.NetCore+Jexus代理+Redis,由于精力有限所以这里只设计到商品添加,抢购,订单查询,处理队列抢购订单的功能:有不 ...

  9. CentOS利用Nginx+Docker部署.netcore应用

    安装docker 官方文档https://docs.docker.com/engine/installation/linux/docker-ce/centos/ [root@sn ~]# yum re ...

随机推荐

  1. interface中setup_time和hold_time

    interface中的setup_time和hold_time input:约束input信号提前T时间采样,然后在时钟沿更新到input信号上. output:约束output信号,在时钟沿T时间后 ...

  2. Spring常用配置使用示例

    上篇介绍了Spring配置的基本情况,本篇介绍Spring常用配置具体如何使用.关于基础的配置,比如Configuration之类的就不示例,主要示例相对用的比较多同时可能比较复杂的标签或属性. 1) ...

  3. 使用Jitpack发布开源Java库

    原文:使用Jitpack发布开源Java库 | Stars-One的杂货小窝 很久之前也写过一篇使用Jitpack发布Android开源库的文章,详见Android开发--发布第三方库到JitPack ...

  4. maven项目 mvn clean install 或 build,报错:Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test

    解决办法 在pom文件中添加

  5. 哪些BI分析商业智能平台是最受欢迎的?

    近些年来,AI推动的云生态系统已非常成熟.智能.增强的预测和决策工具处于这样一个阶段:准备好部署到企业中从董事会到车间的各个地方.挑战在于确保贵企业已准备好使用它们.因此,下面介绍了眼下最出色.最受欢 ...

  6. Redis学习笔记(详细)

    目录 概述 Redis安装启动 常用五大数据类型 Redis键(key) Redis字符串(String) Redis列表(List) Redis集合(Set) Redis哈希(Hash) Redis ...

  7. AndroidMainifest.xml文件属性

    1 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 2 package=" ...

  8. kubernetes配置使用ceph动态存储

    在k8s集群中配置ceph 3.1.使用cephfs (1) 在ceph集群创建cephfs #以下操作在ceph集群的admin或者mon节点上执行 #创建pool来存储数据和元数据 ceph os ...

  9. 转 Autofac怎么依赖注入ASP.NET MVC5类的静态方法

    之前我有介绍过怎么在ASP.NET mvc5中实现的Controller的依赖注入.一般是通过Contrller的构造函数的参数或者属性来注入,但是这有一个共同点就是调用这个类的方法一般都是实例方法, ...

  10. appium滚动查找屏幕外的控件

    嗯,还是把自己做的实验保存一下 Appium1.12.1+python2.7 实验滚动,查找屏幕外控件以及控制seekbar scroll() 是根据页面中两个元素位置之间的距离进行滑动. 滑动寻找屏 ...