netcore后台任务注意事项

博客 分享
0 278
张三
张三 2022-03-27 12:57:13
悬赏:0 积分 收藏

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对象注入不了,必须用ATransientbuilder.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>();  //打印相同的guidbuilder.Services.AddTransient<TransientService>();  //打印不同的guidbuilder.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!

 

posted @ 2022-03-27 12:48 星仔007 阅读(0) 评论(0) 编辑 收藏 举报
回帖
    张三

    张三 (王者 段位)

    821 积分 (2)粉丝 (41)源码

     

    温馨提示

    亦奇源码

    最新会员