在ASP.NET Core请求中,如何将Task添加到后台工作队列并等待结果

hei*_*lch 3 c# asp.net task-queue

我有一个工作服务,它将使用 Cv2 Dnn 执行图像处理。它经过优化以使用所有可用的 CPU,并且运行它的多个实例会降低性能。虽然它运行速度非常快,但如果同时添加两个图像,可能会导致 Dnn 崩溃。

出于这些原因,我希望有一个后台工作人员按顺序从队列中读取数据。所有传入请求都会将其图像添加到此队列并等待结果。

我希望实现的工作流程:

  1. 从API控制器接收图像,将图像推送到队列并等待
  2. 后台worker依次从队列中读取,处理图像,返回结果
  3. 控制器接收处理结果并返回图像

到目前为止,我已经看到了很多将项目推送到队列进行处理的方法,基本上都是“即发即忘”,但没有介绍如何等待项目完成处理。例如,在 Microsoft 关于带有托管服务的后台任务的文档中,他们的排队后台任务示例只是将多个任务推送到队列,然后在添加任务时读取服务,但调用代码不会等待待完成的任务

Gio*_*dze 5

您可以使用TaskCompetitionSouce来实现此目的。考虑这个具有单个后台线程的简单后台服务:

using System;
using System.Threading.Tasks;
using System.Threading;
using System.Collections.Concurrent;


public class BackgroundService
{
    BlockingCollection<ImageQueueItem> _imagesToProcess = new();
    
    
    public BackgroundService()
    {
        var thread = new Thread(ProcessImagesThread);
        thread.Start();
    }
    
    public Task<ImageProcessingResult> ProcessImageQueuedAsync(Image image)
    {
        var taskCompetitionSource = new TaskCompletionSource<ImageProcessingResult>();
        var queueItem = new ImageQueueItem
        {
            ImageToProcess = image,
            ProcessingResult = taskCompetitionSource,
        };
        _imagesToProcess.Add(queueItem);
        return taskCompetitionSource.Task;
    }
    
    
    // background thread that processes images
    private void ProcessImagesThread()
    {
        while(true)
        {
            var queueItem =_imagesToProcess.Take();
            var image = queueItem.ImageToProcess;
            var result = new ImageProcessingResult();
            
            try
            {
                // .............................................................
                // process image here and set some properties to result variable
                // .............................................................

                // complete task by setting result
                queueItem.ProcessingResult.SetResult(result);
            }
            catch(Exception e)
            {
                // or fail task if processing failed
                queueItem.ProcessingResult.SetException(e);
            }
            
        }
    }
}

// Some data about image to process
public class Image{}

// Some data to return from controller after processing image
public class ImageProcessingResult{}

public class ImageQueueItem
{
    public Image ImageToProcess { get; set; }
    public TaskCompletionSource<ImageProcessingResult> ProcessingResult { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

该服务可以这样使用:

// awaiting this will wait until Image is processed
var result = await backgroundService.ProcessImageQueuedAsync()
Run Code Online (Sandbox Code Playgroud)