ASP.NET Core BackgroundService挂起应用程序

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

我需要一个将以一定频率执行的后台服务。我还需要能够唤醒这项服务。为此,我使用

BackgroundService
AutoResetEvent
。完整代码如下

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<WakeUpService>();
builder.Services.AddHostedService<CustomBackgroundService>();
var app = builder.Build();
app.MapWhen(context => context.Request.Path.StartsWithSegments("/WakeUp", StringComparison.OrdinalIgnoreCase), appBuilder =>
{
    appBuilder.Run(async context =>
    {
        context.RequestServices.GetRequiredService<WakeUpService>().WakeUp();
        await context.Response.WriteAsync("WakeUp: " + DateTime.Now);
    });
});
app.MapWhen(_ => true, appBuilder =>
{
    appBuilder.Run(async context => { await context.Response.WriteAsync("OK: " + DateTime.Now); });
});
app.Run();

public class CustomBackgroundService(WakeUpService wakeUpService) : BackgroundService
{
    private int _counter;
    
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            await InternalWorkAsync();
            wakeUpService.Delay();
        }
    }

    private Task InternalWorkAsync()
    {
        Console.WriteLine("Counter: " + _counter++);
        return Task.CompletedTask;
    }
}

public class WakeUpService
{
    private AutoResetEvent ResetEvent { get; } = new(false);
    public void WakeUp() => ResetEvent.Set();
    public void Delay() => ResetEvent.WaitOne(3000);
}

问题是,尽管服务本身启动并工作,但端点(/WakeUp 或任何其他端点)不起作用。但如果我将

ExecuteAsync
包裹在
Task.Run
中,一切都会开始工作。

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    await Task.Run(async () =>
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            await InternalWorkAsync();
            wakeUpService.Delay();
        }
    });
}

为什么会发生这种情况?

Task.Run
的方法有多可靠?

c# asp.net-core async-await
1个回答
0
投票

来自 文档 以及 open Github 问题

ExecuteAsync
变为 异步 之前,不会启动更多服务,例如通过调用
await
。避免在
ExecuteAsync
中执行长时间、阻塞的初始化工作。


AutoResetEvent
挡住了这里。

诀窍是尽快变得异步。

提到的 Github 问题的建议是致电

await Task.Yield()

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    await Task.Yield();

    while (!stoppingToken.IsCancellationRequested)
    {
        await InternalWorkAsync();
        wakeUpService.Delay();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.