.Net核心队列后台任务

时间:2018-06-30 14:11:23

标签: c# events async-await .net-core

Slender回答了我最初的问题,即在发送HTTP响应后,发生了什么事情,然后忘记了,但是现在我剩下的问题是如何正确地对后台任务进行排队

编辑

众所周知,except for in the case when it comes to event handlers通常是异步无效的,我想执行一些后台逻辑而不必等待客户端。我最初的想法是使用“火与忘了”

说我有一个活动:

public class SecondTestClass extends DriverFactory {

    WebDriver driver;

然后有人订阅了一个“忘却”任务:

public event EventHandler LongRunningTask;

网络api方法是调用:

LongRunningTask += async(s, e) => { await LongNetworkOperation;};

但是,如果这样做,我不能保证长时间运行的任务能够完成,如何在不影响发出请求的时间的情况下处理正在运行的后台任务(例如,我不想等待该任务首先完成)?

2 个答案:

答案 0 :(得分:12)

只想在@ johnny5答案中添加一些其他注释。现在,您可以使用https://devblogs.microsoft.com/dotnet/an-introduction-to-system-threading-channels/代替带有信号量的ConcurrentQueue。 该代码将是这样的:

public class HostedService: BackgroundService
{
        private readonly ILogger _logger;
        private readonly ChannelReader<Stream> _channel;

        public HostedService(
            ILogger logger,
            ChannelReader<Stream> channel)
        {
            _logger = logger;
            _channel = channel;
        }

        protected override async Task ExecuteAsync(CancellationToken cancellationToken)
        {
            await foreach (var item in _channel.ReadAllAsync(cancellationToken))
            {
                try
                {
                    // do your work with data
                }
                catch (Exception e)
                {
                    _logger.Error(e, "An unhandled exception occured");
                }
            }
        }
}

[ApiController]
[Route("api/data/upload")]
public class UploadController : ControllerBase
{
    private readonly ChannelWriter<Stream> _channel;

    public UploadController (
        ChannelWriter<Stream> channel)
    {
        _channel = channel;
    }

    public async Task<IActionResult> Upload([FromForm] FileInfo fileInfo)
    {
        var ms = new MemoryStream();
        await fileInfo.FormFile.CopyToAsync(ms);
        await _channel.WriteAsync(ms);
        return Ok();
    }
}

答案 1 :(得分:6)

.Net Core 2.1具有IHostedService,它将在后台安全运行任务。我在QueuedHostedService的{​​{3}}中找到了一个示例,已对其进行了修改以使用BackgroundService。

public class QueuedHostedService : BackgroundService
{

    private Task _backgroundTask;
    private readonly ILogger _logger;

    public QueuedHostedService(IBackgroundTaskQueue taskQueue, ILoggerFactory loggerFactory)
    {
        TaskQueue = taskQueue;
        _logger = loggerFactory.CreateLogger<QueuedHostedService>();
    }

    public IBackgroundTaskQueue TaskQueue { get; }

    protected async override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (false == stoppingToken.IsCancellationRequested)
        {
            var workItem = await TaskQueue.DequeueAsync(stoppingToken);
            try
            {
                await )workItem(stoppingToken);
            }
            catch (Exception ex)
            {
                this._logger.LogError(ex, $"Error occurred executing {nameof(workItem)}.");
            }
        }
    }
}

public interface IBackgroundTaskQueue
{
    void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem);

    Task<Func<CancellationToken, Task>> DequeueAsync(
        CancellationToken cancellationToken);
}

public class BackgroundTaskQueue : IBackgroundTaskQueue
{
    private ConcurrentQueue<Func<CancellationToken, Task>> _workItems =
        new ConcurrentQueue<Func<CancellationToken, Task>>();
    private SemaphoreSlim _signal = new SemaphoreSlim(0);

    public void QueueBackgroundWorkItem(
        Func<CancellationToken, Task> workItem)
    {
        if (workItem == null)
        {
            throw new ArgumentNullException(nameof(workItem));
        }

        _workItems.Enqueue(workItem);
        _signal.Release();
    }

    public async Task<Func<CancellationToken, Task>> DequeueAsync(
        CancellationToken cancellationToken)
    {
        await _signal.WaitAsync(cancellationToken);
        _workItems.TryDequeue(out var workItem);

        return workItem;
    }
}

现在,我们可以在后台安全地将任务排队,而不会影响响应请求所花费的时间。