如果没有等待完成,BackgroundService 永远不会启动/停止

lez*_*lon 5 c# background-service async-await asp.net-core ihostedservice

在 ASP.NET Core 上,我观察到一种奇怪的行为,这实际上是在BackgroundService 中报告的,未关闭,stoppingToken 从未使用 .net core 通用主机设置,但从未找到根本原因

我正在创建以下BackgroundService任务,注册为HostedService

唯一的方法是这样实现的:

 protected override async Task ExecuteAsync(CancellationToken cancellationToken)
 {
     while (!cancellationToken.IsCancellationRequested )
         Console.WriteLine("running");
 }
Run Code Online (Sandbox Code Playgroud)

如果我尝试Ctrl+C或杀死-15这个,它不会停止

如果我像这样更改函数:

protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
    while (!cancellationToken.IsCancellationRequested )
    { 
        Console.WriteLine("running");
        await Task.Delay(1);
    }
}
Run Code Online (Sandbox Code Playgroud)

这是有效的:如果我尝试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;
}
Run Code Online (Sandbox Code Playgroud)

那么,这是怎么回事?

我有两个问题:

  1. 为什么 ExecuteAsync 不在线程池上的另一个线程中从一开始就运行?我假设对的调用_executingTask = ExecuteAsync(_stoppingCts.Token);会立即返回,但显然情况并非如此,它正在等待第一个等待执行之后的行

  2. 我的代码不BackgroundService正确吗?据我所知,在异步函数中使用纯阻塞代码是合法的用例,它不应该导致整个应用程序永远阻塞

Gur*_*ron 6

文档中几乎涵盖了这一点:

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
}
Run Code Online (Sandbox Code Playgroud)

请注意,您的初始实现应该产生一条警告,抱怨缺少,await这暗示您可能正在做一些不完全正确的事情。