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

问题描述 投票:0回答:1

在 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;
}

那么,这是怎么回事?

我有两个问题:

  1. 为什么 ExecuteAsync 不在线程池的另一个线程中从一开始就运行?我假设对

    _executingTask = ExecuteAsync(_stoppingCts.Token);
    的调用会立即返回,但显然情况并非如此,它正在等待第一个等待执行

    之后的行
  2. 我的

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

c# asp.net-core background-service ihostedservice
1个回答
5
投票

这在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
,这暗示您可能正在做一些不完全正确

© www.soinside.com 2019 - 2024. All rights reserved.