Task.Delay() 触发取消应用程序生命周期 [英] Task.Delay() triggers cancellation of application lifetime
问题描述
我遇到过 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屋!