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

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后台任务注意事项的更多相关文章

  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. Solution -「JOISC 2020」「UOJ #509」迷路的猫

    \(\mathcal{Decription}\)   Link.   这是一道通信题.   给定一个 \(n\) 个点 \(m\) 条边的连通无向图与两个限制 \(A,B\).   程序 Anthon ...

  2. 阿里云人脸1:N搜索开源版-Java版(文末附开源地址)

    ​ 一.人脸检测相关概念 人脸检测(Face Detection)是检测出图像中人脸所在位置的一项技术,是人脸智能分析应用的核心组成部分,也是最基础的部分.人脸检测方法现在多种多样,常用的技术或工具大 ...

  3. 中国首个进入谷歌编程之夏(GSoC)的开源项目: Casbin, 2022 年预选生招募!

    Casbin 明日之星预选生计划-Talent for Casbin 2022 "Casbin 明日之星预选生计划-Talent for Casbin 2022"是什么? &quo ...

  4. jmeter变量嵌套:__V

    问题复现 ${name_${n}} 下面没有获取到结果 解决方案 __V是用于执行变量名表达式 ${__V(name_${n})} 获取到结果

  5. 零基础小白也能用的商业智能BI工具,自助式就是香!

    ​随着数字化时代的到来,数据已经成为企业无形的资源,企业对员工的数据分析能力也提出了新的要求.掌握一定的数据分析能力无疑会大大增加自己在职场中的竞争力,但并不是所有人都具备专业的数据分析基础,尤其是虽 ...

  6. 我对maptask 和 reducetask的理解

    MapTask: 首先经过 FileInputFormat 判断该文件是否要进行切片,如果是我们自定义的FileInputFormat基本上重写isSplit方法返回为false表示不进行切片,那么就 ...

  7. pip:各项指令

    学习自:(7条消息) pip 常用命令及控制台怎么查看python 及pip 和已安装包版本号_peiwang245的博客-CSDN博客_查看pip版本 1.版本及升级 版本:pip -V 升级:pi ...

  8. JS报错:Uncaught TypeError: Cannot set property ‘nTf‘ of undefined

    在使用DataTable时,遇到以下报错: Uncaught TypeError: Cannot set property 'nTf' of undefined ... ... 初步排查后发现是< ...

  9. Tableau怎么制作专业图表

    Tableau怎么制作专业图表 本文首发于博客冰山一树Sankey,去博客浏览效果更好.直接右上角搜索该标题即可 一. 统计表 1.1 不同种类的图表风格 商业周刊的图表风格 经济学人的图表风格 华尔 ...

  10. appium ios 相对坐标点击和控件滑动操作

    环境: 系统:ios 10.13.6 (17G12034) appium:1.14.0 xcode:10.1 iphone:iphone7 12.4 在尝试使用driver.tap([(a,b)],5 ...