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

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. 通过shell脚本统计elasticsearch indices每天的数量以及大小

    前情提要: 最近elasticsearch集群总出问题,之前虽然修复了,现在又出现新的问题,于是PM要求拉取elasticsearch每天建立的索引有多少,索引有多大,需要对机器进行评估 客户现场无法 ...

  2. c++ istream_iterator ostream_iterator

    istream_iterator/ostream_iterator void stream_iter_odd_even(const string &in_file, const string ...

  3. 在超算系统上使用sbatch提交MXNet分布式训练任务

    在超算系统上运行MXNet分布式训练任务时,面临着一个IP地址相关的问题.我们在提交MXNet的分布式任务时,需要知道各个GPU节点的IP地址,把这些IP地址放到一个hosts文件中,以供分布式训练使 ...

  4. mysql is not null 执行效率低

    a表字段类型为int b表字段类型为varchar a left join b时如果查询条件是is not null,效率很快,is  null 效率很低 后来两者改为同类型 int,效率很高

  5. 来自牛逼哥的阴间MD5(web)

    这个web题目是来自队里面牛逼哥的题目,审计源码, 看到这两个参数,前面的a和b就是直接输出数字,再看下面的,需要弱比较的输出一个c,要求应该是需要一个加密之前是一个0e开头的字符串,加密之后还是0e ...

  6. msf常见命令

    msf命令全集 一.msfconsole ?   帮助菜单 back 从当前环境返回 banner   显示一个MSF banner cd   切换目录 color   颜色转换 connect   ...

  7. python基础之数值类型与序列类型

    Hello大家好,我是python学习者小杨同学,已经学习python有一段时间,今天将之前学习过的内容整理一番,在这与大家分享与交流,现在开始我们的python基础知识之旅吧. 数值类型与序列类型 ...

  8. 在Linux发行版上使用7zip的方法

    学习如何在 Ubuntu 和其他 Linux 发行版中安装和使用 7zip 7zip介绍 7Zip(更适当的写法是 7-Zip)是一种在 Windows 用户中广泛流行的归档格式.一个 7Zip 归档 ...

  9. C#的泛型和Java的伪泛型

    C#的泛型和java的伪泛型,talk is cheap,show me the code   C#泛型 下面结果,C#里面会输出false,如果这个还不能真正的说明C#的泛型是真的泛型,那就看下面这 ...

  10. C# Winform中FpSpread表格控件设置固定的(冻结的)行或列

    在项目中我们经常会用到固定表头的操作,FpSpread提供了冻结行或列的属性. 你可以冻结表单中的行或列(使其不可滚动). 你可以冻结任意个表单顶部的行,使其成为前导行,你也可以冻结左侧任意多个列,使 ...