Task.Delay() 触发取消应用程序生命周期
Task.Delay() triggers cancellation of application lifetime
我遇到过 Task.Delay()
方法会在 IApplicationLifetime
中触发取消事件的情况。这是代码:
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();
}
这里是异常停止的托管服务:
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
。尽管从理论上讲,我没有明确中止应用程序的代码。
日志将如下所示:
Starting BrowserWorkerHostedService is working. 09.07.2019 17:03:53
EmailWorkerHostedService is working. 09.07.2019 17:03:53
Application started. Press Ctrl+C to shut down.
Hosting environment: Production
Content root path: xxxx
End EmailWorkerHostedService
End BrowserWorkerHostedService
是否有很好的解释为什么 Task.Delay()
触发 ApplicationStarted 取消事件?
您在滥用 IApplicationLifetime
事件。它们的目的是让您能够将某些操作与它们相关联。例如,您希望仅在应用程序完全启动时才开始监听消息队列。你要这样做:
_applicationLifetime.ApplicationStarted. Register(StartListenMq);
我认为在这里使用 CancellationTokens
不是最好的主意,但它是实现的方式。
当您想取消 HostedService
时,您应该只检查在 ExecuteAsync
方法中收到的令牌。流程将如下所示:
IApplicationLifetime.StopApplication()
=> 将触发 IApplicationLifetime.ApplicationStopping
=> 将触发 IHostedService.StopAsync()
=> 将 stopToken
现在回答您的问题:为什么它会发生在 await Task.Delay()
?再看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
.
我遇到过 Task.Delay()
方法会在 IApplicationLifetime
中触发取消事件的情况。这是代码:
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();
}
这里是异常停止的托管服务:
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
。尽管从理论上讲,我没有明确中止应用程序的代码。
日志将如下所示:
Starting BrowserWorkerHostedService is working. 09.07.2019 17:03:53
EmailWorkerHostedService is working. 09.07.2019 17:03:53
Application started. Press Ctrl+C to shut down.
Hosting environment: Production
Content root path: xxxx
End EmailWorkerHostedService
End BrowserWorkerHostedService
是否有很好的解释为什么 Task.Delay()
触发 ApplicationStarted 取消事件?
您在滥用 IApplicationLifetime
事件。它们的目的是让您能够将某些操作与它们相关联。例如,您希望仅在应用程序完全启动时才开始监听消息队列。你要这样做:
_applicationLifetime.ApplicationStarted. Register(StartListenMq);
我认为在这里使用 CancellationTokens
不是最好的主意,但它是实现的方式。
当您想取消 HostedService
时,您应该只检查在 ExecuteAsync
方法中收到的令牌。流程将如下所示:
IApplicationLifetime.StopApplication()
=> 将触发 IApplicationLifetime.ApplicationStopping
=> 将触发 IHostedService.StopAsync()
=> 将 stopToken
现在回答您的问题:为什么它会发生在 await Task.Delay()
?再看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
.