Task.Delay() 触发取消应用程序生命周期 [英] Task.Delay() triggers cancellation of application lifetime

查看:19
本文介绍了Task.Delay() 触发取消应用程序生命周期的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到过 Task.Delay() 方法会在 IApplicationLifetime 中触发取消事件的情况.代码如下:

I have faced a situation when Task.Delay() method would trigger an event of cancellation in IApplicationLifetime. Here is the code:

    static async Task Main(string[] args)
    {
        Console.WriteLine("Starting");

       await BuildWebHost(args)
            .RunAsync();
        
        Console.WriteLine("Press any key to exit..");
        Console.ReadKey();
    }

    private static IHost BuildWebHost(string[] args)
    {
        var hostBuilder = new HostBuilder()
            .ConfigureHostConfiguration(config =>
            {
                config.AddEnvironmentVariables();
                config.AddCommandLine(args);
            })
            .ConfigureAppConfiguration((hostContext, configApp) =>
            {
                configApp.SetBasePath(Directory.GetCurrentDirectory());
                configApp.AddCommandLine(args);
            })
            .ConfigureServices((hostContext, services) =>
            {
                services.AddHostedService<BrowserWorkerHostedService>();
                services.AddHostedService<EmailWorkerHostedService>();
            })
            .UseConsoleLifetime();

        return hostBuilder.Build();
    }

这里是异常停止的托管服务:

and here are hosted services which are stopping abnormally:

public class BrowserWorkerHostedService : BackgroundService
{
    private IApplicationLifetime _lifetime;
    private IHost _host;

    public BrowserWorkerHostedService(
        IApplicationLifetime lifetime,
        IHost host)
    {
        this._lifetime = lifetime;
        this._host = host;
    }

    protected override async Task ExecuteAsync(CancellationToken stopToken)
    {
        while (!_lifetime.ApplicationStarted.IsCancellationRequested
            && !_lifetime.ApplicationStopping.IsCancellationRequested
            && !stopToken.IsCancellationRequested)
        {
            Console.WriteLine($"{nameof(BrowserWorkerHostedService)} is working. {DateTime.Now.ToString()}");
            
            //lifetime.StopApplication();
            //await StopAsync(stopToken);

            await Task.Delay(1_000, stopToken);
        }

        Console.WriteLine($"End {nameof(BrowserWorkerHostedService)}");

        await _host.StopAsync(stopToken);
    }
}

public class EmailWorkerHostedService : BackgroundService
{
    private IApplicationLifetime _lifetime;
    private IHost _host = null;

    public EmailWorkerHostedService(
        IApplicationLifetime lifetime,
        IHost host)
    {
        this._lifetime = lifetime;
        this._host = host;
    }

    protected override async Task ExecuteAsync(CancellationToken stopToken)
    {
        while (!_lifetime.ApplicationStarted.IsCancellationRequested
            && !_lifetime.ApplicationStopping.IsCancellationRequested
            && !stopToken.IsCancellationRequested)
        {
            Console.WriteLine($"{nameof(EmailWorkerHostedService)} is working. {DateTime.Now.ToString()}");

            await Task.Delay(1_000, stopToken);
        }

        Console.WriteLine($"End {nameof(EmailWorkerHostedService)}");
        
        await _host.StopAsync(stopToken);
    }
}

我希望我的服务能够运行,除非 lifetime.StopApplication() 被触发.但是,托管服务已停止,因为 lifetime.ApplicationStarted.IsCancellationRequested 变量在第二次迭代时设置为 true .. 尽管理论上,我没有明确的代码中止应用程序.

I would like my services to be running, unless lifetime.StopApplication() is triggered. However, hosted services are stopped, because lifetime.ApplicationStarted.IsCancellationRequested variable is set to true upon a second itteration.. Even though, in theory, I have no code that explicitly aborts the application.

日志将如下所示:

正在启动 BrowserWorkerHostedService.09.07.2019 17:03:53

Starting BrowserWorkerHostedService is working. 09.07.2019 17:03:53

EmailWorkerHostedService 正在运行.09.07.2019 17:03:53

EmailWorkerHostedService is working. 09.07.2019 17:03:53

应用程序已启动.按 Ctrl+C 关闭.

Application started. Press Ctrl+C to shut down.

托管环境:生产

内容根路径:xxxx

结束 EmailWorkerHostedService

End EmailWorkerHostedService

结束 BrowserWorkerHostedService

End BrowserWorkerHostedService

有没有很好的解释为什么 Task.Delay() 会触发 ApplicationStarted 取消事件?

Is there a good explanation why Task.Delay() triggers ApplicationStarted cancellation event?

推荐答案

您正在滥用 IApplicationLifetime 事件.它们的目的是让您能够将某些操作与它们相关联.例如,您只想在应用程序完全启动时启动消息队列侦听.你会这样做:

You are misusing IApplicationLifetime events. Their purpose is to give you the ability to associate some action with them. For example you want to start message queue listening only when you application is fully started. You are going to do it like this:

_applicationLifetime.ApplicationStarted. Register(StartListenMq);

我认为在这里使用 CancellationTokens 并不是最好的主意,但它的实现方式是这样的.

I think using CancellationTokens here wasn't the best idea, but it the way it was implemented.

当您想取消您的 HostedService 时,您应该只检查在 ExecuteAsync 方法中收到的令牌.流程看起来像这样:

When you want to cancel your HostedService you should check only token received in ExecuteAsync method. The flow will look look this:

IApplicationLifetime.StopApplication() => 将触发 IApplicationLifetime.ApplicationStopping => 将触发 IHostedService.StopAsync() => 将 stopToken

IApplicationLifetime.StopApplication() => will trigger IApplicationLifetime.ApplicationStopping => will trigger IHostedService.StopAsync() => will stopToken

现在问你的问题:为什么它发生在 await Task.Delay() 上?再看BackgroundService.StartAsync()

And now for your question: why it happens on await Task.Delay()? Look again at BackgroundService.StartAsync()

    public virtual Task StartAsync(CancellationToken cancellationToken)
    {
        // Store the task we're executing
        _executingTask = ExecuteAsync(_stoppingCts.Token);

        // If the task is completed then return it, this will bubble cancellation and failure to the caller
        if (_executingTask.IsCompleted)
        {
            return _executingTask;
        }

        // Otherwise it's running
        return Task.CompletedTask;
    }

此代码不会等待 ExecuteAsync.在您在代码中调用异步操作的那一刻 StartAsync() 将继续运行.在它的调用堆栈中的某个地方,它将触发 ApplicationStarted 并且由于您正在收听它,因此您将获得 _lifetime.ApplicationStarted.IsCancellationRequested = true.

This code does't await ExecuteAsync. At the moment you call async operation in your code StartAsync() will continue to run. Somewhere in it's callstack it will trigger ApplicationStarted and since you are listening to it you will get _lifetime.ApplicationStarted.IsCancellationRequested = true.

这篇关于Task.Delay() 触发取消应用程序生命周期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆