ASP.NET Core长时间运行/后台任务

26
以下是在Asp.Net Core中实施长时间运行的后台工作的正确模式吗?或者我应该使用一些形式的Task.Run/TaskFactory.StartNewTaskCreationOptions.LongRunning选项?
    public void Configure(IApplicationLifetime lifetime)
    {
        lifetime.ApplicationStarted.Register(() =>
        {
            // not awaiting the 'promise task' here
            var t = DoWorkAsync(lifetime.ApplicationStopping);

            lifetime.ApplicationStopped.Register(() =>
            {
                try
                {
                    // give extra time to complete before shutting down
                    t.Wait(TimeSpan.FromSeconds(10));
                }
                catch (Exception)
                {
                    // ignore
                }
            });
        });
    }

    async Task DoWorkAsync(CancellationToken token)
    {
        while (!token.IsCancellationRequested)
        {
            await // async method
        }
    }

13
您可以使用像 Hangfire 这样的库来完成这个任务。 - NtFreX
1
有关IApplicationLifetime的更多详细信息,请参见https://www.khalidabuhakmeh.com/looking-at-asp-net-cores-iapplicationlifetime - Michael Freidgeim
2个回答

29

请查看 .NET Core 2.0 IHostedService。这是文档。从 .NET Core 2.1 开始,我们将拥有BackgroundService抽象类。 可以像这样使用:

public class UpdateBackgroundService: BackgroundService
{
    private readonly DbContext _context;

    public UpdateTranslatesBackgroundService(DbContext context)
    {
        this._context= context;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await ...
    }
}
在你的创业公司中,你只需要注册类:
public static IServiceProvider Build(IServiceCollection services)
{
    //.....
    services.AddSingleton<IHostedService, UpdateBackgroundService>();
    services.AddTransient<IHostedService, UpdateBackgroundService>();  //For run at startup and die.
    //.....
}

在针对完整的 .Net Framework 时,它也会可用吗? - sroll
是的,因为IHostedService接口所在的托管库可用在netstandard2.0包中。 - alsami
@Makla 这个服务是在另一个线程上运行吗?因为我的请求直到服务完成之前都没有完成。 - Darem
1
@Darem 不是的。在返回任务之前,执行会被阻塞。但如果你使用async await,应该是安全的。链接 还有一个 SO - Makla
1
你能否像数据库ID一样将参数传递到ExecuteAsync中? - Jess

23

以下是在Asp.Net Core中实现长时间运行后台工作的正确模式吗?

是的,这是在ASP.NET Core上启动长时间运行工作的基本方法。您肯定不应该使用Task.Run/StartNew/LongRunning - 这种方法一直是错误的。

请注意,您的长时间运行的工作可能会随时关闭,这是正常的。如果您需要更可靠的解决方案,则应该在ASP.NET之外拥有单独的后台系统(例如Azure函数/AWS lambdas)。也有像Hangfire这样的库,可以为您提供一些可靠性,但它们也有自己的缺点。

更新: 我写了一个关于如何实现更可靠的方法的博客系列,使用具有独立后台系统的持久队列。根据我的经验,大多数开发人员需要更可靠的方法,因为他们不想失去长时间运行的工作。


谢谢Stephen - 我对这种方法的疑虑正是你提到的“工作可能随时关闭”。假设我不让异常从异步方法中抛出,运行时何时会关闭异步工作呢? - ubi
3
@ubi:ASP.NET 偶尔会进行回收以保持清洁。同时也有常规原因:操作系统更新、云编排移动、电源中断等。 - Stephen Cleary
再次感谢。您能详细解释一下ASP.NET Core的回收吗?我在其他地方没有看到过(除了IIS应用程序池回收)。我将其作为控制台应用程序运行。运行时会重新启动应用程序还是请求管道(发出“ApplicationStopping/Stopped”信号并再次调用“Startup”中的“Configure”方法或类似的操作)? - ubi
1
@ubi:啊,既然你是自托管的,它就不会被回收了。但其他原因仍然存在。 - Stephen Cleary
1
@StephenCleary 我有点困惑,难道任何形式的重启不都是再次运行整个应用程序代码吗?在这种情况下,通过 Task.Run 启动的后台任务只会重新开始吗?或者你实际上是指使用 Task.Run 时我们无法对后台任务进行优雅关闭?但是,在停电(以及其他各种情况)的情况下,这似乎也不可能。因此,我们必须为此做好准备。 - freakish
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接