繁体   English   中英

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

[英]Parallel queued background tasks with hosted services in ASP.NET Core

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

我目前正在严格遵循Microsoft提供的教程,当尝试模拟工作负载时,从同一个用户发出多个请求以排队任务我注意到所有workItem都按顺序执行,因此没有并行性。

我的问题是,这种行为是否有望? 如果是这样,为了使请求执行并行,可以解雇并忘记,而不是等待workItem完成?

我已经搜索了几天没有运气的特定情况,所以如果有人提供任何指南或示例,我会很高兴。

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

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

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)}.");
            }
        }
    }

    ...
}

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

我在执行工作项时尝试了火灾和忘记方法,它按照我的预期方式工作,同时并行执行的几个任务,我不确定这是否是一个好的做法,或者是否有一个更好或更好地处理这种情况的方法。

您发布的代码按顺序执行排队项目, 一次一个,但也与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()");

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

Event 1
Event 2
...
Event 9
Event 10

为了引入并行性,我们必须在QueuedHostedService更改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);
  }
}

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

Event 0
Event 1
Event 2
Event 3
Event 4
Event 5
Event 7
Event 6
Event 9
Event 8

编辑: 在生产环境中以这种方式执行代码是否可以,无需等待它?

我认为大多数开发人员遇到“一劳永逸”问题的原因是它经常被滥用。

当你使用fire-and-forget执行一个Task ,你基本上告诉我你不关心这个函数的结果。 你不在乎它是否成功退出,是否被取消或是否引发了异常。 但对于大多数Task确实关心结果。

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

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

在我看来,这就是它。 困难的部分是找到一个Task ,你真的不关心Task的结果。

您可以为计算机中的每个CPU添加一次或两次QueuedHostedService。

所以像这样:

for (var i=0;i<Environment.ProcessorCount;++i)
{
    services.AddHostedService<QueuedHostedService>();
}

您可以在扩展方法中隐藏它,并使并发级别可配置以保持干净。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM