Я работаю с некоторыми WIFI
устройствами, например, с камерами.
Базовый собрат, который я реализовал:
- Кто-то нажимает кнопку.
- Кнопка вызывает мою конечную точку
Web API
. - Моя конечная точка
Web API
вызывает одну изAPI's
камеры (авторHttpRequest
). - Обработка каждого запроса занимает 5 секунд. Причем между каждым запросом должна быть задержка в 1 секунду. Например, если вы нажимаете кнопку 2 раза с задержкой в одну секунду после каждого: сначала мы ожидаем 5 секунд для обработки первого нажатия, затем одну секунду задержки и в конце мы ожидаем 5 секунд для последнего процесса (второе нажатие).
Для этого я использую Queued background tasks
на основе Fire and Forgot
в проекте .NetCore 3.1
, и он отлично работает, когда я имею дело только с одной камерой.
Но новое требование проекта: фоновая задача должна обрабатывать несколько камер. Это означает, что одна очередь на камеру, и очереди должны работать параллельно, в зависимости от того, что я описал выше.
Например, если у нас есть 2 устройства camera-001
и camera-002
и 2 подключенные кнопки btn-cam-001
и btn-cam-002
, И порядок нажатия (задержка 0,5 с после каждого нажатия): 2X btn-cam-001 и 1X btn-cam-002.
На самом деле происходит FIFO
. Сначала будут обработаны запросы btn-cam-001
, а затем btn-cam-002
.
Что я ожидаю и что мне нужно: Camera-002
не следует ждать получения запроса, и первые запросы к обеим камерам _17 _ / _ 18_ должны обрабатываться одновременно (на основе примера). Вроде у каждой камеры своя очередь и свой процесс.
Вопрос в том, как этого добиться в .NetCore 3.1? Ценю любую помощь.
Моя текущая фоновая служба:
public class QueuedHostedService : BackgroundService
{
public IBackgroundTaskQueue TaskQueue { get; }
private readonly ILogger _logger;
public QueuedHostedService(IBackgroundTaskQueue taskQueue, ILoggerFactory loggerFactory)
{
TaskQueue = taskQueue;
_logger = loggerFactory.CreateLogger<QueuedHostedService>();
}
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Queued Hosted Service is starting.");
while (!cancellationToken.IsCancellationRequested)
{
var workItem = await TaskQueue.DequeueAsync(cancellationToken);
try
{
await workItem(cancellationToken);
}
catch (Exception exception)
{
_logger.LogError(exception, $"Error occurred executing {nameof(workItem)}.");
}
}
_logger.LogInformation("Queued Hosted Service is stopping.");
}
}
И текущий BackgroundTaskQueue:
public class BackgroundTaskQueue : IBackgroundTaskQueue
{
private readonly SemaphoreSlim _signal = new SemaphoreSlim(0);
private readonly ConcurrentQueue<Func<CancellationToken, Task>> _workItems =
new ConcurrentQueue<Func<CancellationToken, Task>>();
public void QueueBackgroundWorkItem(Func<CancellationToken, Task> workItem)
{
if (workItem is 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;
}
}
Моя текущая конечная точка:
[HttpPost("hit")]
public ActionResult TurnOnAsync([FromBody] HitRequest request, CancellationToken cancellationToken = default)
{
try
{
var camera = ConfigurationHelper.GetAndValidateCamera(request.Device, _configuration);
_taskQueue.QueueBackgroundWorkItem(async x =>
{
await _cameraRelayService.TurnOnAsync(request.Device, cancellationToken);
Thread.Sleep(TimeSpan.FromSeconds(1));
});
return Ok();
}
catch (Exception exception)
{
_logger.LogError(exception, "Error when truning on the lamp {DeviceName}.", request.Device);
return StatusCode(StatusCodes.Status500InternalServerError, exception.Message);
}
}
TaskQueue.QueueBackgroundWorkItem
? - person Theodor Zoulias   schedule 21.05.2020ExecuteAsync
, кажется, ведет себя непоследовательно в случае отмены. ВозвращенныйTask
может либо перейти в состояниеCanceled
(если отмена происходит на любом из этаповawait
), либо в состояниеRanToCompletion
(если отмена происходит во время проверкиIsCancellationRequested
в циклеwhile
). - person Theodor Zoulias   schedule 21.05.2020Thread.Sleep(TimeSpan.FromSeconds(1));
вы можете рассмотреть более легкийawait Task.Delay(TimeSpan.FromSeconds(1));
, чтобы избежать блокировкиThreadPool
потока. - person Theodor Zoulias   schedule 21.05.2020