首页 > 解决方案 > 如何在 ASPNET Core 3.0 中启动后台进程?

问题描述

我知道新的 ASPNET Core 3.0 堆栈围绕托管进程进行了许多改进。

我很好奇能够从 Razor PageModel 定义和执行后台进程的最佳方式?这意味着我有一些逻辑需要在后台启动某些东西,然后 Razor 页面不需要监视它的结果,但如果这不是太难的话,我也希望能够观察它。

有人可以给我看一个代码示例或指出我正确的方向吗?

标签: c#asp.net-corerazor-pages

解决方案


由于这可能是您之前关于IHostedService的问题的后续问题,因此我假设您希望在 ASP.NET Core 应用程序中拥有一些能够执行后台任务的后台服务(作为托管服务)。现在您想通过控制器或 Razor 页面操作触发这样的任务并让它在后台执行?

一个常见的模式是有一些中央存储来跟踪后台服务和 Web 应用程序都可以访问的任务。一个简单的方法是让它成为双方都可以访问的(线程安全的)单例服务。

文档实际上显示了一个简单的示例,使用的BackgroundTaskQueue正是共享服务/状态。如果你有一个特定类型的工作的工人,你也可以像这样实现它:

public class JobQueue<T>
{
    private readonly ConcurrentQueue<T> _jobs = new ConcurrentQueue<T>();
    private readonly SemaphoreSlim _signal = new SemaphoreSlim(0);

    public void Enqueue(T job)
    {
        _jobs.Enqueue(job);
        _signal.Release();
    }

    public async Task<T> DequeueAsync(CancellationToken cancellationToken = default)
    {
        await _signal.WaitAsync(cancellationToken);
        _jobs.TryDequeue(out var job);
        return job;
    }
}

然后,您可以使用服务集合以及在此队列上工作的托管后台服务注册此实现:

services.AddSingleton<JobQueue<MyJob>>();
services.AddHostedService<MyJobBackgroundService>();

该托管服务的实现可能如下所示:

public class MyJobBackgroundService : BackgroundService
{
    private readonly ILogger<MyJobBackgroundService> _logger;
    private readonly JobQueue<MyJob> _queue;

    public MyJobBackgroundService(ILogger<MyJobBackgroundService> logger, JobQueue<MyJob> queue)
    {
        _logger = logger;
        _queue = queue;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            var job = await _queue.DequeueAsync(stoppingToken);

            // do stuff
            _logger.LogInformation("Working on job {JobId}", job.Id);
            await Task.Delay(2000);
        }
    }
}

在控制器操作或 Razor 页面模型中,您只需注入JobQueue<MyJob>并调用Enqueue它即可将作业添加到列表中。一旦后台服务准备好处理它,它就会处理它。

最后请注意,队列显然是在内存中的,因此如果您的应用程序关闭,尚未完成的作业列表也会消失。如果需要,您当然也可以将这些信息保存在数据库中,并从数据库中设置队列。


推荐阅读