使用 ASP.NET Core 中的托管服务并行排队后台任务

mar*_*atg 13 c# parallel-processing asp.net-core-2.1 asp.net-core-hosted-services

我正在使用 ASP.NET Core 2.1 版中的托管服务功能对新的后台任务进行一些测试,更具体地说是使用排队后台任务,我想到了一个关于并行性的问题。

我目前严格遵循 Microsoft 提供的教程,并且在尝试模拟来自同一用户的多个请求以将任务排入队列时的工作负载时,我注意到所有工作项都是按顺序执行的,因此没有并行性。

我的问题是,这种行为是预期的吗?如果是这样,为了使请求执行并行,是否可以触发并忘记,而不是等待 workItem 完成?

我在没有运气的情况下搜索了几天有关此特定场景的信息,因此如果有人提供任何指南或示例,我会非常高兴。

编辑:教程中的代码很长,所以它的链接是https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.1#queued -背景任务

执行工作项的方法是这样的:

public class QueuedHostedService : IHostedService
{
    ...

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Queued Hosted Service is starting.");

        _backgroundTask = Task.Run(BackgroundProceessing);

        return Task.CompletedTask;
    }

    private async Task BackgroundProceessing()
    {
        while (!_shutdown.IsCancellationRequested)
        {
            var workItem = 
                await TaskQueue.DequeueAsync(_shutdown.Token);

            try
            {
                await workItem(_shutdown.Token);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, 
                    $"Error occurred executing {nameof(workItem)}.");
            }
        }
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

问题的重点是要知道是否有人可以分享有关如何使用此特定技术同时执行多个工作项的知识,因为服务器可以处理此工作负载。

我在执行工作项时尝试了即发即弃的方法,它按照我的预期工作,同时并行执行多个任务,我不确定这是否是一个好的做法,或者是否有处理这种情况的更好或正确的方法。

a-c*_*tor 8

您发布的代码按顺序执行排队的项目,一次一个,但也与 Web 服务器并行。一个IHostedService正在按照定义与 Web 服务器并行运行。这篇文章提供了一个很好的概述。

考虑以下示例:

_logger.LogInformation ("Before()");
for (var i = 0; i < 10; i++)
{
  var j = i;
  _backgroundTaskQueue.QueueBackgroundWorkItem (async token =>
  {
    var random = new Random();
    await Task.Delay (random.Next (50, 1000), token);
    _logger.LogInformation ($"Event {j}");
  });
}
_logger.LogInformation ("After()");
Run Code Online (Sandbox Code Playgroud)

我们添加了 10 个等待随机时间的任务。如果您将代码放在控制器方法中,即使控制器方法返回,事件仍将被记录。但是每个项目都将按顺序执行,以便输出如下所示:

Event 1
Event 2
...
Event 9
Event 10
Run Code Online (Sandbox Code Playgroud)

为了引入并行,我们必须改变的执行BackgroundProceessing的方法QueuedHostedService


这是一个允许并行执行两个任务的示例实现:

private async Task BackgroundProceessing()
{
  var semaphore = new SemaphoreSlim (2);

  void HandleTask(Task task)
  {
    semaphore.Release();
  }

  while (!_shutdown.IsCancellationRequested)
  {
    await semaphore.WaitAsync();
    var item = await TaskQueue.DequeueAsync(_shutdown.Token);

    var task = item (_shutdown.Token);
    task.ContinueWith (HandleTask);
  }
}
Run Code Online (Sandbox Code Playgroud)

使用此实现,由于每个任务等待随机时间量,因此记录的事件顺序不再按顺序排列。所以输出可能是:

Event 0
Event 1
Event 2
Event 3
Event 4
Event 5
Event 7
Event 6
Event 9
Event 8
Run Code Online (Sandbox Code Playgroud)

编辑:在生产环境中以这种方式执行代码而不等待它可以吗?

我认为大多数开发人员遇到“即发即忘”问题的原因是它经常被误用。

当您执行Task使用即发即弃时,您基本上是在告诉我您不关心此函数的结果。您不关心它是否成功退出、是否被取消或是否抛出异常。但是对于大多数Tasks,您确实关心结果。

  • 您确实希望确保数据库写入通过
  • 您确实希望确保将日志条目写入硬盘驱动器
  • 您确实希望确保将网络数据包发送到接收器

如果你关心结果,Task那么即发即忘就是错误的方法

在我看来就是这样。困难的部分是找到一个Task你真的不关心Task.