forked from ddrilling/AsbCloudServer
17c13b7a7b
nit refactoring by VS recommendations
124 lines
3.8 KiB
C#
124 lines
3.8 KiB
C#
using Microsoft.Extensions.DependencyInjection;
|
||
using Microsoft.Extensions.Hosting;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics;
|
||
using System.Linq;
|
||
using System.Threading;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace AsbCloudInfrastructure.Background;
|
||
|
||
/// <summary>
|
||
/// Сервис для фонового выполнения работы
|
||
/// </summary>
|
||
public class BackgroundWorker : BackgroundService
|
||
{
|
||
private readonly TimeSpan minDelay = TimeSpan.FromSeconds(1);
|
||
private readonly IServiceProvider serviceProvider;
|
||
|
||
/// <summary>
|
||
/// Очередь работ
|
||
/// </summary>
|
||
private Queue<Work> works = new(8);
|
||
|
||
/// <summary>
|
||
/// Список периодических работ
|
||
/// </summary>
|
||
public IEnumerable<Work> Works => works;
|
||
|
||
/// <summary>
|
||
/// Работа выполняемая в данный момент
|
||
/// </summary>
|
||
public Work? CurrentWork;
|
||
private bool isRuning;
|
||
|
||
/// <summary>
|
||
/// последние 16 завершившиеся с ошибкой
|
||
/// </summary>
|
||
public CyclicArray<Work> Felled { get; } = new(16);
|
||
|
||
/// <summary>
|
||
/// последние 16 успешно завершенных
|
||
/// </summary>
|
||
public CyclicArray<Work> Done { get; } = new(16);
|
||
|
||
/// <summary>
|
||
/// Ошибка в главном цикле, никогда не должна появляться
|
||
/// </summary>
|
||
public string MainLoopLastException { get; private set; } = string.Empty;
|
||
|
||
public BackgroundWorker(IServiceProvider serviceProvider)
|
||
{
|
||
this.serviceProvider = serviceProvider;
|
||
}
|
||
|
||
protected override async Task ExecuteAsync(CancellationToken token)
|
||
{
|
||
if (isRuning)
|
||
return;
|
||
isRuning = true;
|
||
Trace.TraceInformation($"{GetType().Name} started");
|
||
while (!token.IsCancellationRequested && works.TryDequeue(out CurrentWork))
|
||
{
|
||
try
|
||
{
|
||
using var scope = serviceProvider.CreateScope();
|
||
|
||
var result = await CurrentWork.Start(scope.ServiceProvider, token);
|
||
|
||
if (!result)
|
||
Felled.Add(CurrentWork);
|
||
else
|
||
Done.Add(CurrentWork);
|
||
|
||
CurrentWork = null;
|
||
await Task.Delay(minDelay, token);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
MainLoopLastException = $"BackgroundWorker " +
|
||
$"MainLoopLastException: \r\n" +
|
||
$"date: {DateTime.Now:O}\r\n" +
|
||
$"message: {ex.Message}\r\n" +
|
||
$"inner: {ex.InnerException?.Message}\r\n" +
|
||
$"stackTrace: {ex.StackTrace}";
|
||
Trace.TraceError(MainLoopLastException);
|
||
}
|
||
}
|
||
isRuning = false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Добавить в очередь
|
||
/// <para>
|
||
/// work.Id может быть не уникальным,
|
||
/// при этом метод TryRemoveFromQueue удалит все работы с совпадающими id
|
||
/// </para>
|
||
/// </summary>
|
||
/// <param name="work"></param>
|
||
public void Enqueue(Work work)
|
||
{
|
||
works.Enqueue(work);
|
||
if (ExecuteTask is null || ExecuteTask.IsCompleted)
|
||
StartAsync(CancellationToken.None);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Удаление работы по ID из одноразовой очереди
|
||
/// </summary>
|
||
/// <param name="id"></param>
|
||
/// <returns></returns>
|
||
public bool TryRemoveFromQueue(string id)
|
||
{
|
||
var work = Works.FirstOrDefault(w => w.Id == id);
|
||
if (work is not null)
|
||
{
|
||
works = new Queue<Work>(Works.Where(w => w.Id != id));
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
}
|