在 ASP.NET Core 上,我观察到一种奇怪的行为,这实际上是在 BackgroundService 未关闭,stoppingToken 从未使用 .net core 通用主机设置 中报告的,但从未找到根本原因
我正在创建以下
BackgroundService
任务,注册为 HostedService
:
唯一的方法是这样实现的:
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested )
Console.WriteLine("running");
}
如果我尝试 Ctrl+C 或杀死 -15 这个,它不会停止。
如果我像这样更改功能:
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested )
{
Console.WriteLine("running");
await Task.Delay(1);
}
}
这有效:如果我尝试 Ctrl+C 这个程序,则会设置取消令牌,然后退出。
如果我返回到不起作用的版本并暂停它,我会看到即使我处于 ExecuteAsync 方法中,它下面的框架也是 StartAsync(),在这种情况下它永远不会完成!
我看到的 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 不在线程池的另一个线程中从一开始就运行?我假设对
_executingTask = ExecuteAsync(_stoppingCts.Token);
的调用会立即返回,但显然情况并非如此,它正在等待第一个等待执行之后的行
我的
BackgroundService
代码是否不正确?据我所知,在异步函数中使用纯粹的阻塞代码是一个合法的用例,它不应该导致整个应用程序永远阻塞
这在docs中几乎已涵盖:
被调用来运行后台服务。该实现返回一个ExecuteAsync(CancellationToken)
,代表后台服务的整个生命周期。在System.Threading.Tasks.Task
变为异步(例如通过调用ExecuteAsync
)之前,不会启动其他服务。避免在await
中执行长时间、阻塞的初始化工作。ExecuteAsync
简单的修复方法是在
await Task.Yield()
开始时调用 ExecuteAsync
:
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
await Task.Yield();
// ... rest of the code
}
请注意,您的初始实现应该会产生一条警告,抱怨缺少
await
,这暗示您可能正在做一些不完全正确。