Безопасно ли помещать Задачу с зависимостями служб в заданную очередь в очередь Singleton? - PullRequest
0 голосов
/ 23 сентября 2019

Я в значительной степени использую пример, предоставленный в документации Microsoft для постановки в очередь фоновых задач .

В этой очереди я добавляю Func<Task>, который будет выполнен позже QueuedHostedService.

Контроллер - HTTP POST

...
Func<Task> workItem = () => _mockService.DoWorkAsync(guid);
_queue.QueueBackgroundWorkItem(workItem);
return Ok();

MockService.DoWorkAsync

var data = await _insideMockService.GetAsync();
await _anotherService.Notify(data);

BackgroundTaskQueue.QueueBackgroundWorkItem

private ConcurrentQueue<Func<Task>> _workItems
private SemaphoreSlim _signal = new SemaphoreSlim(0);
public void QueueBackgroundWorkItem(Func<Task> workItem)
{
    if (workItem == null)
    {
        throw new ArgumentNullException(nameof(workItem));
    }
    _workItems.Enqueue(workItem);
    _signal.Release();
}

QueuedHostedService.Executeync

_currentTask = await _queue.DequeueAsync(cancellationToken);
try
{
    await _currentTask();
...

BackgroundTaskQueue.DequeueAsync

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

Startup.ConfigureServices

services.AddScoped<IMockService, MockService>(); // Implements DoWorkAsync
services.AddScoped<IInsideMockService, InsideMockService>(); // DoWorkAsync requires this dependency 
services.AddScoped<IAnotherService, AnotherService>(); // DoWorkAsync requires this dependency 
services.AddHostedService<QueuedHostedService>();
services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();

IMockService.DoWorkAsync метод использования scoped служб.Ссылка на этот метод добавляется в очередь, которая находится в службе singleton.Эта очередь позже читается HostedService, который также singleton.

Есть ли вероятность того, что сервисные ссылки в DoWorkAsync будут удалены перед обработкой HostedService?Если исключить сценарии, из-за которых приложение изящно (или неблагодарно) закрывается.

Некоторые локальные тестовые прогоны, возможно, с сотней запросов (и некоторые добавленные Task.Delay в DoWorkAsync), кажется, работают нормально, но яя не уверен, что я что-то упустил ..

1 Ответ

1 голос
/ 23 сентября 2019

Основная проблема здесь не в том, что ссылка на Func будет собираться garbade.

Проблема заключается в том, что любой используемый им сервис области действия может быть удален.

Когда запросы заканчиваются, область действия удаляется.со всем его одноразовым содержанием.Если ваши сервисы будут реализовывать IDisposable - они тоже будут утилизированы.И ваша задача в очереди попытается вызвать удаленные службы и завершится неудачей.

И даже если ваша служба не будет реализовывать сам IDisposable - она ​​может использовать, прямо или косвенно, какой-либо другой объект IDisposable, который будет быть утилизированным (например, DbContext).

Чтобы убедиться, что этот материал не выйдет из строя в один прекрасный день - вы должны тщательно контролировать все используемые объекты / сервисы (и их подобъекты / подуслуги)во время DoWorkAsync.В этом случае - почему бы вам не зарегистрировать MockService istelf как синглтон?:)

Правильным способом для фоновой задачи является захват экземпляра IServiceScopeFactory и вызов CreateScope () перед выполнением любого реального задания, а также получение из него любых сервисов области действия.

...