ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

netcore后台任务注意事项

2022-03-27 13:00:38  阅读:221  来源: 互联网

标签:netcore builder tickerService System 注意事项 Services using 后台任务 public


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

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,builder,tickerService,System,注意事项,Services,using,后台任务,public
来源: https://www.cnblogs.com/morec/p/16062509.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有