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

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. Java基于ClassLoder/ InputStream 配合读取配置文件

    阅读java开源框架源码或者自己开发系统时配置文件是一个不能忽略的,在阅读开源代码的过程中尝尝困惑配置文件是如何被读取到内存中的.配置文件本身只是为系统运行提供参数的支持,个人阅读源码时重点不大可能放 ...

  2. Javers 比较两个类的差异

    Javers 在开发过程中遇到需求,比较数据库中的原数据与新修改要写入库中的数据.这个实体类是比较复杂的.例如有基本类型,BigDecimal类型,自定义类型,Date类型,List集合,Set集合, ...

  3. 认识 LLVM

    简介 LLVM是一套提供编译器基础设施的开源项目,是用 C++ 编写,包含一系列模块化的编译器组件和工具链,用来开发编译器前端和后端.它是为了任意一种编程语言而写成的程序,利用虚拟技术创造出编译时期. ...

  4. NSSCTF-[SWPU 2019]伟大的侦探

    下载附件得到一个压缩包,解压需要密码,但是得到一个"密码.txt"的文件,打开查看 根据菜狗的刷题经验,这是个EBCDIC的编码,打开010编辑器,打开"密码.txt&q ...

  5. 宿主机ping不通虚拟机,虚拟机能ping通宿主机

    最近,微信提升群里好几个小伙伴遇到了如题的问题. 问了下原因,原来是我说的把宿主机网卡ip获取方式改为自动,结果他们把宿主机上虚拟网卡的ip改为自动了. 当然,分析"宿主机ping不通虚拟机 ...

  6. [Matlab]二维隐函数绘图

    MATLAB提供了一个ezplot函数绘制隐函数图形,有三种调用方式: 对于函数f=f(x),ezplot函数的调用格式为: ezplot(f):在默认区间-2pi<=x<=2pi内绘制f ...

  7. 说出来你可能不信,我用excel就能做一张高端的统计报表

    统计报表是指各级企事业.行政单位按规定的表格形式.内容.时间要求报送程序,自上而下统一布置,提供统计资料的一种统计调查方式.统计报表具有来源可靠.回收率高.方式灵活等特点,是各个基层企业或事业单位填报 ...

  8. 用MySQL碰到的一些“坑”

    本篇文章持续更新. 这里说坑,也不算坑,只是对我一个经常用SQL Server的来说有点不习惯而已. 一.GroupBy 的不同 create table Customer ( CustomerNum ...

  9. .NET Core WebApi使用Swagger

    1.新建Core Api项目,引用Swashbuckle.AspNetCore 包 配置Startup.cs类的 using System; using System.Collections.Gene ...

  10. 01--c实现基础客户端和服务端与c++ boost.asio实现对比

    c实现服务端和客户端交互: 学习查阅的博客: https://blog.csdn.net/u011068702/article/details/54380259 https://blog.csdn.n ...