繁体   English   中英

.Net核心队列后台任务

[英].Net Core Queue Background Tasks

在发送HTTP响应之后,Slender回答了我关于触发和忘记发生的事情的原始问题,但现在我留下了如何正确排队后台任务的问题

编辑

众所周知,Async void通常很糟糕, 除非涉及事件处理程序 ,我想执行一些后台逻辑而不必让客户端等待。 我最初的想法是使用Fire和Forget

说我有一个活动:

public event EventHandler LongRunningTask;

然后有人订阅了火灾并忘记了任务:

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

web api方法是调用:

[HttpGet]
public async IActionResult GetTask()
{
    LongRunningTask?.Invoke(this, EventArgs.Empty);
    return Ok();
}

但如果我这样做,我的长期运行任务不能保证完成,我如何处理正在运行的后台任务而不影响我的请求所需的时间(例如我不想等待任务先完成)?

.Net Core 2.1有一个IHostedService,可以在后台安全地运行任务。 我在QueuedHostedService 文档中找到了一个示例,我已经修改了它以使用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;
    }
}

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

暂无
暂无

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

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