代码之家  ›  专栏  ›  技术社区  ›  Jamil

如何在启动时创建任务并在应用程序停止时停止它?

  •  0
  • Jamil  · 技术社区  · 6 年前

    我在.net core中使用mvc,我需要在开始时运行一个任务,并在应用程序停止时停止它。在Startup.cs中,我注册了应用程序启动和停止的事件。问题是,我不知道如何运行一个任务,必须在一个特定的类从启动运行。任务如下:

    public void PreventStatusChange()
        {
            while (forceStatusChange)
            {
                foreach (var ext in GetExtensions())
                {
                    ext.Status = StatusType.Available;
                }
                Thread.Sleep(1000);
            }
        }
    

    变量forceStatusChange是在同一个类中声明的,因此我在Startup.cs中看不到它。最好的方法是什么?

    2 回复  |  直到 6 年前
        1
  •  5
  •   Panagiotis Kanavos    6 年前

    您需要创建一个实现 IHostedService . 这个接口只定义了两个方法,StartAsync,它在应用程序启动时调用,并且 StopAsync 当它终止时调用。

    您需要将其注册为托管服务:

    services.AddHostedService<TimedHostedService>();
    

    小心使用 AddHostedService , 不是 AddSingleton . 如果你使用 地址单例 运行时不知道在适当的时候调用StartAsync和StopAsync。

    文章 Background tasks with hosted services in ASP.NET Core 演示如何使用计时器实现服务:

    internal class TimedHostedService : IHostedService, IDisposable
    {
        private readonly ILogger _logger;
        private Timer _timer;
    
        public TimedHostedService(ILogger<TimedHostedService> logger)
        {
            _logger = logger;
        }
    
        public Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("Timed Background Service is starting.");
    
            _timer = new Timer(DoWork, null, TimeSpan.Zero, 
                TimeSpan.FromSeconds(10));
    
            return Task.CompletedTask;
        }
    
        private void DoWork(object state)
        {
            _logger.LogInformation("Timed Background Service is working.");
        }
    
        public Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("Timed Background Service is stopping.");
    
            _timer?.Change(Timeout.Infinite, 0);
    
            return Task.CompletedTask;
        }
    
        public void Dispose()
        {
            _timer?.Dispose();
        }
    }
    

    这段代码没有什么特别有趣的地方-只要在 StartAsync 被叫停了 停止异步

    取消长时间运行的任务

    当运行库需要回收或停止时,它将调用 停止异步 方法在所有托管服务上,等待它们正常完成,然后警告它们立即取消。过一段时间后,它将继续并终止应用程序或回收它。

    这个 cancellationToken 参数用于指示服务应立即停止。通常,这意味着您必须编写自己的代码来检查它,警告自己的任务终止,等待所有任务完成等等,与代码类似 shown in this article

    不过,这基本上是样板文件,这就是为什么 BackgroundService 类可用于创建只需实现 ExecuteAsync(CancellationToken) . 启动和停止该任务由 BackgroundService ,例如:

    public class PollingService : BackgroundService
    {
    
        private readonly ILogger _logger;
    
        public PollingService(ILogger<PollingService> logger)
        {
            _logger = logger;
        }
    
        protected async override Task ExecuteAsync(
            CancellationToken cancellationToken)
        {
    
            while (!cancellationToken.IsCancellationRequested)
            {
    
                try
                {
                    await DoSomething(cancellationToken);
                    await Task.Delay(1000,cancellationToken);
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, 
                       $"Error occurred executing {nameof(workItem)}.");
                }
            }
    
            _logger.LogInformation("Queued Hosted Service is stopping.");
        }        
    }
    

    在这种情况下 Task.Delay() 一旦运行时引发取消标记,它本身将被取消。 DoSomething() 本身应该以检查取消标记的方式实现,例如将其传递给任何接受它作为参数的异步方法,测试 IsCancellationRequested 属性在每个循环中退出,并将其退出。

    例如:

        protected async override Task ExecuteAsync(
            CancellationToken cancellationToken)
        {
    
            while (!cancellationToken.IsCancellationRequested)
            {
                try
                {
                    foreach (var ext in GetExtensions())
                    {
                        //Oops, time to cancel
                        if(cancellationToken.IsCancellationRequested)
                        {
                            break;
                        }
                        //Otherwise, keep working
                        ext.Status = StatusType.Available;
                    }
                    await Task.Delay(1000,cancellationToken);
                }
                catch (Exception ex)
                {
                    ...
                }
            }
    
            _logger.LogInformation("Hosted Service is stopping.");
        }        
    
        2
  •  1
  •   Alex Riabov Vikrant    6 年前

    你可以用 BackgroundService

    public class LongRunningService : BackgroundService
    { 
        public LongRunningService()
        {
        }
    
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested && forceStatusChange)
            {
                foreach (var ext in GetExtensions())
                {
                    ext.Status = StatusType.Available;
                }
    
                await Task.Delay(1000, stoppingToken);
            }  
        }
    
        protected override async Task StopAsync (CancellationToken stoppingToken)
        {
            // Run your graceful clean-up actions
        }
    }
    

    并注册:

    public void ConfigureServices(IServiceCollection services)
    {
       ...
       services.AddSingleton<IHostedService, LongRunningService>();
       ...
    }