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

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

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

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

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


慕雪6442864
浏览 222回答 2
2回答

慕妹3146593

您发布的代码按顺序执行排队的项目,一次一个,但也与 Web 服务器并行。一个IHostedService正在按照定义与 Web 服务器并行运行。这篇文章提供了一个很好的概述。考虑以下示例:_logger.LogInformation ("Before()");for (var i = 0; i < 10; i++){&nbsp; var j = i;&nbsp; _backgroundTaskQueue.QueueBackgroundWorkItem (async token =>&nbsp; {&nbsp; &nbsp; var random = new Random();&nbsp; &nbsp; await Task.Delay (random.Next (50, 1000), token);&nbsp; &nbsp; _logger.LogInformation ($"Event {j}");&nbsp; });}_logger.LogInformation ("After()");我们添加了 10 个等待随机时间的任务。如果您将代码放在控制器方法中,即使控制器方法返回,事件仍将被记录。但是每个项目都将按顺序执行,以便输出如下所示:Event 1Event 2...Event 9Event 10为了引入并行,我们必须改变的执行BackgroundProceessing的方法QueuedHostedService。这是一个允许并行执行两个任务的示例实现:private async Task BackgroundProceessing(){&nbsp; var semaphore = new SemaphoreSlim (2);&nbsp; void HandleTask(Task task)&nbsp; {&nbsp; &nbsp; semaphore.Release();&nbsp; }&nbsp; while (!_shutdown.IsCancellationRequested)&nbsp; {&nbsp; &nbsp; await semaphore.WaitAsync();&nbsp; &nbsp; var item = await TaskQueue.DequeueAsync(_shutdown.Token);&nbsp; &nbsp; var task = item (_shutdown.Token);&nbsp; &nbsp; task.ContinueWith (HandleTask);&nbsp; }}使用此实现,由于每个任务等待随机时间量,因此记录的事件顺序不再按顺序排列。所以输出可能是:Event 0Event 1Event 2Event 3Event 4Event 5Event 7Event 6Event 9Event 8编辑:在生产环境中以这种方式执行代码而不等待它可以吗?我认为大多数开发人员遇到“即发即忘”问题的原因是它经常被误用。当您执行Task使用即发即弃时,您基本上是在告诉我您不关心此函数的结果。您不关心它是否成功退出、是否被取消或是否抛出异常。但是对于大多数Tasks,您确实关心结果。您确实希望确保数据库写入通过您确实希望确保将日志条目写入硬盘驱动器您确实希望确保将网络数据包发送到接收器如果你关心结果,Task那么即发即忘就是错误的方法。在我看来就是这样。困难的部分是找到一个Task你真的不关心Task.

慕少森

您可以为机器中的每个 CPU 添加一次或两次 QueuedHostedService。所以像这样:for (var i=0;i<Environment.ProcessorCount;++i){&nbsp; &nbsp; services.AddHostedService<QueuedHostedService>();}您可以将其隐藏在扩展方法中,并使并发级别可配置以保持整洁。
打开App,查看更多内容
随时随地看视频慕课网APP