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

时间:2018-08-06 10:13:40

标签: c# asp.net-mvc asp.net-core

我正在使用带有.net核心的mvc,我需要在启动时运行一个任务,并在应用程序停止时停止它。在Startup.cs中,我注册了应用程序启动和停止的事件。问题是,我不知道如何从启动时运行必须在特定类中运行的任务。任务看起来像这样:

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

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

2 个答案:

答案 0 :(得分:5)

您需要创建一个实现IHostedService的类。该接口仅定义两个方法,StartAsync在应用程序启动时调用,StopAsync在应用程序终止时调用。

您需要使用以下命令将其注册为托管服务:

services.AddHostedService<TimedHostedService>();

请小心使用AddHostedService不要 AddSingleton。如果您使用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时启动一个计时器,然后在StopAsync上停止它即可

取消长期运行的任务

当运行时需要回收或停止时,它将在所有托管服务上调用StopAsync方法,稍等片刻使其正常运行,然后警告其立即取消。片刻之后,它将继续并终止应用程序或回收它。

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属性并退出引发的令牌。 / p>

例如:

    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.");
    }        

答案 1 :(得分:1)

您可以使用BackgroundService

ALLOWED_HOSTS = ['192.168.45.2', 'localhost', '127.0.0.1','192.168.1.2']

并注册:

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
    }
}