netcore后台任务注意事项
开局一张图,故事慢慢编!这是一个后台任务打印时间的德莫,代码如下:
- using BackGroundTask;
- var builder = WebApplication.CreateBuilder();
- builder.Services.AddTransient<TickerService>();
- builder.Services.AddHostedService<TickerBackGroundService>();
- builder.Build().Run();
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace BackGroundTask
- {
- internal class TickerService
- {
- private event EventHandler<TickerEventArgs> Ticked;
- public TickerService()
- {
- Ticked += OnEverySecond;
- Ticked += OnEveryFiveSecond;
- }
- public void OnEverySecond(object? sender,TickerEventArgs args)
- {
- Console.WriteLine(args.Time.ToLongTimeString());
- }
- public void OnEveryFiveSecond(object? sender, TickerEventArgs args)
- {
- if(args.Time.Second %5==0)
- Console.WriteLine(args.Time.ToLongTimeString());
- }
- public void OnTick(TimeOnly time)
- {
- Ticked?.Invoke(this, new TickerEventArgs(time));
- }
- }
- internal class TickerEventArgs
- {
- public TimeOnly Time { get; }
- public TickerEventArgs(TimeOnly time)
- {
- Time = time;
- }
- }
- }
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace BackGroundTask
- {
- internal class TickerBackGroundService : BackgroundService
- {
- private readonly TickerService _tickerService;
- public TickerBackGroundService(TickerService tickerService)
- {
- _tickerService = tickerService;
- }
- protected override async Task ExecuteAsync(CancellationToken stoppingToken)
- {
- while (!stoppingToken.IsCancellationRequested)
- {
- _tickerService.OnTick(TimeOnly.FromDateTime(DateTime.Now));
- await Task.Delay(1000,stoppingToken);
- }
- }
- }
- }
结果和预期一样,每秒打印一下时间,五秒的时候会重复一次。
代码微调,把打印事件改成打印guid,新增TransientService类:
- internal class TransientService
- {
- public Guid Id { get; }=Guid.NewGuid();
- }
微调后代码如下:
- using BackGroundTask;
- var builder = WebApplication.CreateBuilder();
- builder.Services.AddTransient<TickerService>();
- builder.Services.AddTransient<TransientService>(); //新增生成guid类
- builder.Services.AddHostedService<TickerBackGroundService>();
- builder.Build().Run();
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace BackGroundTask
- {
- internal class TickerService
- {
- private event EventHandler<TickerEventArgs> Ticked;
- private readonly TransientService _transientService; //注入TransientService
- public TickerService(TransientService transientService)
- {
- Ticked += OnEverySecond;
- Ticked += OnEveryFiveSecond;
- _transientService = transientService;
- }
- public void OnEverySecond(object? sender,TickerEventArgs args)
- {
- Console.WriteLine(_transientService.Id); //打印guid
- }
- public void OnEveryFiveSecond(object? sender, TickerEventArgs args)
- {
- if(args.Time.Second %5==0)
- Console.WriteLine(args.Time.ToLongTimeString());
- }
- public void OnTick(TimeOnly time)
- {
- Ticked?.Invoke(this, new TickerEventArgs(time));
- }
- }
- internal class TickerEventArgs
- {
- public TimeOnly Time { get; }
- public TickerEventArgs(TimeOnly time)
- {
- Time = time;
- }
- }
- }
TickerBackGroundService类没有做改动,来看看结果:
看似没问题,但是这个guid每次拿到的是一样的,再来看注入的TransientService类,是瞬时的,而且TickerService也是瞬时的。那应该每次会拿到新的对象新的guid才对。那这个后台任务是不是满足不了生命周期控制的要求呢?
问题就出在下面的代码上:
- while (!stoppingToken.IsCancellationRequested)
- {
- _tickerService.OnTick(TimeOnly.FromDateTime(DateTime.Now));
- await Task.Delay(1000,stoppingToken);
- }
任务只要不停止,循环会一直下去,所以构造函数注入的类不会被释放,除非程序重启。那么怎么解决这个问题呢,那就是在while里面每次每次循环都创建一个新的对象。那就可以引入ServiceProvider对象。改造后的代码如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace ConsoleBackGround
- {
- internal class GlobalService
- {
- public static IServiceProvider ServiceProvider { get; set; }
- }
- }
- using ConsoleBackGround;
- var builder = WebApplication.CreateBuilder();
- builder.Services.AddTransient<TransientService>(); //Guid相同
- //builder.Services.AddSingleton<TransientService>(); //构造函数使用Guid相同,使用scope对象注入不了,必须用ATransient
- //builder.Services.AddScoped<TransientService>(); //构造函数使用Guid相同, 使用scope对象注入不了,必须用ATransient
- builder.Services.AddTransient<TickerService>();
- GlobalService.ServiceProvider = builder.Services.BuildServiceProvider(); //一定要在注入之后赋值,要不然只会拿到空对象。
- builder.Services.AddHostedService<TickerBackGroundService>();
- builder.Build().Run();
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace ConsoleBackGround
- {
- internal class TickerBackGroundService : BackgroundService
- {
- //private readonly TickerService _tickerService;
- //public TickerBackGroundService(TickerService tickerService)
- //{
- // _tickerService = tickerService;
- //}
- protected override async Task ExecuteAsync(CancellationToken stoppingToken)
- {
- while (!stoppingToken.IsCancellationRequested)
- {
- //_tickerService.OnTick(TimeOnly.FromDateTime(DateTime.Now)); //guid不会变
- using var scope = GlobalService.ServiceProvider.CreateScope();
- var _tickerService = scope.ServiceProvider.GetService<TickerService>();
- _tickerService?.OnTick(TimeOnly.FromDateTime(DateTime.Now)); //可以保证guid会变
- await Task.Delay(1000,stoppingToken);
- }
- }
- }
- }
问题出在循环上所以TickerService代码不需要做任何更改。针对方便构造函数注入serviceprovider的情况完全不需要全局的GlobalService,通过构造函数注入的代码如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace ConsoleBackGround
- {
- internal class TickerBackGroundService : BackgroundService
- {
- private readonly IServiceProvider _sp;
- public TickerBackGroundService(IServiceProvider sp)
- {
- _sp = sp;
- }
- protected override async Task ExecuteAsync(CancellationToken stoppingToken)
- {
- while (!stoppingToken.IsCancellationRequested)
- {
- ////_tickerService.OnTick(TimeOnly.FromDateTime(DateTime.Now)); //guid不会变
- using var scope = _sp.CreateScope();
- var _tickerService = scope.ServiceProvider.GetService<TickerService>();
- _tickerService?.OnTick(TimeOnly.FromDateTime(DateTime.Now)); //可以保证guid会变
- await Task.Delay(1000,stoppingToken);
- }
- }
- }
- }
运行结果符合预期:
下面看看使用MediatR的代码,也可以达到预期:
- using BackGroundMediatR;
- using MediatR;
- Console.Title = "BackGroundMediatR";
- var builder = WebApplication.CreateBuilder();
- //builder.Services.AddSingleton<TransientService>(); //打印相同的guid
- builder.Services.AddTransient<TransientService>(); //打印不同的guid
- builder.Services.AddMediatR(typeof(Program));
- builder.Services.AddHostedService<TickerBackGroundService>();
- builder.Build().Run();
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace BackGroundMediatR
- {
- internal class TransientService
- {
- public Guid Id { get; }=Guid.NewGuid();
- }
- }
- using MediatR;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace BackGroundMediatR
- {
- internal class TimedNotification:INotification
- {
- public TimeOnly Time { get; set; }
- public TimedNotification(TimeOnly time)
- {
- Time = time;
- }
- }
- }
- using MediatR;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace BackGroundMediatR
- {
- internal class EventSecondHandler : INotificationHandler<TimedNotification>
- {
- private readonly TransientService _service;
- public EventSecondHandler(TransientService service)
- {
- _service = service;
- }
- public Task Handle(TimedNotification notification, CancellationToken cancellationToken)
- {
- Console.WriteLine(_service.Id);
- return Task.CompletedTask;
- }
- }
- }
- using MediatR;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace BackGroundMediatR
- {
- internal class EveryFiveSecondHandler : INotificationHandler<TimedNotification>
- {
- public Task Handle(TimedNotification notification, CancellationToken cancellationToken)
- {
- if(notification.Time.Second % 5==0)
- Console.WriteLine(notification.Time.ToLongTimeString());
- return Task.CompletedTask;
- }
- }
- }
- using MediatR;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- namespace BackGroundMediatR
- {
- internal class TickerBackGroundService : BackgroundService
- {
- private readonly IMediator _mediator;
- public TickerBackGroundService(IMediator mediator)
- {
- _mediator = mediator;
- }
- protected override async Task ExecuteAsync(CancellationToken stoppingToken)
- {
- while (!stoppingToken.IsCancellationRequested)
- {
- var timeNow = TimeOnly.FromDateTime(DateTime.Now);
- await _mediator.Publish(new TimedNotification(timeNow));
- await Task.Delay(1000,stoppingToken);
- }
- }
- }
- }
执行结果如下:
代码链接:
exercise/Learn_Event at master · liuzhixin405/exercise (github.com)
Over!
netcore后台任务注意事项的更多相关文章
- 【5min+】后台任务的积木。.NetCore中的IHostedService
系列介绍 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net ...
- .netcore开发环境和服务器注意事项
对于开发环境,如果你需要使用.netcore命令的话,你需要安装SDK:如果你还需要运行.netcore的网站的话,你必须还要安装它的[runtime]和[hosting server]: 对于服务器 ...
- Swagger/OpenAPI By Swashbuckle在NetCore 3.1中较NetCore 2.2使用的注意事项及入门
方案选择 使用Web API时,了解其各种方法对开发人员来说可能是一项挑战. Swagger也称为OpenAPI(Open Application Programming Interface,开放应用 ...
- 在.netcore webapi项目中使用后台任务工具Hangfire
安装Hangfire 在webapi项目中通过nuget安装Hangfire.Core,Hangfire.SqlServer,Hangfire.AspNetCore,截止到目前的最新版本是1.7.6. ...
- 开发Adobe AIR移动应用程序的考虑事项
http://www.adobe.com/cn/devnet/air/articles/considerations-air-apps-mobile.html Adobe AIR 经过发展演进,已经超 ...
- 与众不同 windows phone (13) - Background Task(后台任务)之后台文件传输(上传和下载)
原文:与众不同 windows phone (13) - Background Task(后台任务)之后台文件传输(上传和下载) [索引页][源码下载] 与众不同 windows phone (13) ...
- ubuntu16.04-x64系统中Jexus web server部署.NetCore和端口分析引发的猜想!
您有这样的牢骚么? 有一周没更新博客了,简单说下在干什么吧:主要是公司安排对接某旅游大公司的接口,接口数量倒也就10个左右,对接完后还需要加入到业务系统中和App端,因此还是需要花点时间的:时间上来说 ...
- .NetCore+Jexus代理+Redis模拟秒杀商品活动
开篇叙 本篇将和大家分享一下秒杀商品活动架构,采用的架构方案正如标题名称.NetCore+Jexus代理+Redis,由于精力有限所以这里只设计到商品添加,抢购,订单查询,处理队列抢购订单的功能:有不 ...
- CentOS利用Nginx+Docker部署.netcore应用
安装docker 官方文档https://docs.docker.com/engine/installation/linux/docker-ce/centos/ [root@sn ~]# yum re ...
随机推荐
- interface中setup_time和hold_time
interface中的setup_time和hold_time input:约束input信号提前T时间采样,然后在时钟沿更新到input信号上. output:约束output信号,在时钟沿T时间后 ...
- Spring常用配置使用示例
上篇介绍了Spring配置的基本情况,本篇介绍Spring常用配置具体如何使用.关于基础的配置,比如Configuration之类的就不示例,主要示例相对用的比较多同时可能比较复杂的标签或属性. 1) ...
- 使用Jitpack发布开源Java库
原文:使用Jitpack发布开源Java库 | Stars-One的杂货小窝 很久之前也写过一篇使用Jitpack发布Android开源库的文章,详见Android开发--发布第三方库到JitPack ...
- maven项目 mvn clean install 或 build,报错:Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test
解决办法 在pom文件中添加
- 哪些BI分析商业智能平台是最受欢迎的?
近些年来,AI推动的云生态系统已非常成熟.智能.增强的预测和决策工具处于这样一个阶段:准备好部署到企业中从董事会到车间的各个地方.挑战在于确保贵企业已准备好使用它们.因此,下面介绍了眼下最出色.最受欢 ...
- Redis学习笔记(详细)
目录 概述 Redis安装启动 常用五大数据类型 Redis键(key) Redis字符串(String) Redis列表(List) Redis集合(Set) Redis哈希(Hash) Redis ...
- AndroidMainifest.xml文件属性
1 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 2 package=" ...
- kubernetes配置使用ceph动态存储
在k8s集群中配置ceph 3.1.使用cephfs (1) 在ceph集群创建cephfs #以下操作在ceph集群的admin或者mon节点上执行 #创建pool来存储数据和元数据 ceph os ...
- 转 Autofac怎么依赖注入ASP.NET MVC5类的静态方法
之前我有介绍过怎么在ASP.NET mvc5中实现的Controller的依赖注入.一般是通过Contrller的构造函数的参数或者属性来注入,但是这有一个共同点就是调用这个类的方法一般都是实例方法, ...
- appium滚动查找屏幕外的控件
嗯,还是把自己做的实验保存一下 Appium1.12.1+python2.7 实验滚动,查找屏幕外控件以及控制seekbar scroll() 是根据页面中两个元素位置之间的距离进行滑动. 滑动寻找屏 ...