C# ASP.Net核心排队后台任务并行处理
我有一个ASP.NET核心Web API,它使用如下所述的排队后台任务 我使用了提供的代码示例并添加了C# ASP.Net核心排队后台任务并行处理,c#,asp.net,asp.net-core,parallel-processing,asp.net-core-hosted-services,C#,Asp.net,Asp.net Core,Parallel Processing,Asp.net Core Hosted Services,我有一个ASP.NET核心Web API,它使用如下所述的排队后台任务 我使用了提供的代码示例并添加了IBackgroundTaskQueue、BackgroundTaskQueue和QueuedHostedService,与本文中描述的完全相同 在我的Startup.cs中,我只注册了一个QueuedHostedService实例,如下所示:services.AddHostedService() 来自WebApi控制器的任务排队,然后由QueuedHostedService逐个执行 我希望允
IBackgroundTaskQueue
、BackgroundTaskQueue
和QueuedHostedService
,与本文中描述的完全相同
在我的Startup.cs
中,我只注册了一个QueuedHostedService
实例,如下所示:services.AddHostedService()代码>
来自WebApi控制器的任务排队,然后由QueuedHostedService
逐个执行
我希望允许多个后台处理线程出列并执行传入任务。
我能想到的最直接的解决方案是在我的Startup.cs
中注册多个QueuedHostedService
实例。i、 e,类似这样的:
int maxNumOfParallelOperations;
var isValid = int.TryParse(Configuration["App:MaxNumOfParallelOperations"], out maxNumOfParallelOperations);
maxNumOfParallelOperations = isValid && maxNumOfParallelOperations > 0 ? maxNumOfParallelOperations : 2;
for (int index = 0; index < maxNumOfParallelOperations; index++)
{
services.AddHostedService<QueuedHostedService>();
}
int-maxNumof并行操作;
var isValid=int.TryParse(配置[“App:MaxNumOfParallelOperations”],out-MaxNumOfParallelOperations);
maxNumOfParallelOperations=isValid&&maxNumOfParallelOperations>0?MaxNumof并行运算:2;
for(int index=0;index
我还注意到,由于BackgroundTaskQueue
中的信号量,QueuedHostedService
实例并非一直都在工作,而是只有在队列中有新任务可用时才会唤醒
在我的测试中,这个解决方案似乎运行得很好
但是,在这个特定的用例中,它真的是一个有效的、推荐的并行处理解决方案吗?您可以使用带有多个线程的IHostedService
来使用IBackgroundTaskQueue
这里是一个基本的实现。我假设您使用的是所述的IBackgroundTaskQueue
和BackgroundTaskQueue
另外,您可以设置配置中的线程数(appsettings.json
)
使用QueuedHostedService是最好的解决方案。不幸的是,您不能完全控制线程池。考虑并行度不是由您所使用的托管服务的数量给出,而是由线程池的线程数给出。如果您使用10个线程,则可以并行执行10个操作。这些线程与webhost共享,我认为asp.net核心将优先考虑web请求而不是后台任务。好的,谢谢,我知道了。但是,为并行处理注册同一后台任务的多个实例(如本文所述)是一种好做法吗?我的方向是最多注册5个,以便在我的服务中实现基本的并行性。
public class QueuedHostedService : IHostedService
{
private readonly ILogger _logger;
private readonly Task[] _executors;
private readonly int _executorsCount = 2; //--default value: 2
private CancellationTokenSource _tokenSource;
public IBackgroundTaskQueue TaskQueue { get; }
public QueuedHostedService(IBackgroundTaskQueue taskQueue,
ILoggerFactory loggerFactory,
IConfiguration configuration)
{
TaskQueue = taskQueue;
_logger = loggerFactory.CreateLogger<QueuedHostedService>();
if (ushort.TryParse(configuration["App:MaxNumOfParallelOperations"], out var ct))
{
_executorsCount = ct;
}
_executors = new Task[_executorsCount];
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Queued Hosted Service is starting.");
_tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
for (var i = 0; i < _executorsCount; i++)
{
var executorTask = new Task(
async () =>
{
while (!cancellationToken.IsCancellationRequested)
{
#if DEBUG
_logger.LogInformation("Waiting background task...");
#endif
var workItem = await TaskQueue.DequeueAsync(cancellationToken);
try
{
#if DEBUG
_logger.LogInformation("Got background task, executing...");
#endif
await workItem(cancellationToken);
}
catch (Exception ex)
{
_logger.LogError(ex,
"Error occurred executing {WorkItem}.", nameof(workItem)
);
}
}
}, _tokenSource.Token);
_executors[i] = executorTask;
executorTask.Start();
}
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Queued Hosted Service is stopping.");
_tokenSource.Cancel(); // send the cancellation signal
if (_executors != null)
{
// wait for _executors completion
Task.WaitAll(_executors, cancellationToken);
}
return Task.CompletedTask;
}
}
...
services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
services.AddHostedService<QueuedHostedService>();
...
...
"App": {
"MaxNumOfParallelOperations": 4
}
...