C# Контроль фоновых Task
В программе есть фоновая задача (ожидание запросов через HttpListener), внутри этой задачи при поступлении запроса создаю задачу которая обрабатывает сам контекст запроса. Задачи по обработке контекста запроса складываю в HashSet, запрос может обрабатываться долго (до 5 сек.) и в HashSet периодически копятся задачи. Хотел запустить фоновую задачу для контроля над HashSet, т.е. получать первую завершенную задачу, удалять ее из HashSet и логировать результат завершения задачи
StartListen() - запускает все задачи, но на момент старта запросы не поступали и HashSet пуста, поэтому ожидать через WhenAny первую завершенную задачу не получается.
я могу в цикле вручную, например раз в 100мс проверять элементы HashSet и искать там завершенные задачи.
Вот у меня и вопрос это делается в ручную или как то иначе можно?
private readonly HashSet<Task<Result>> _httpContextTasks = new HashSet<Task<Result>>();
public Result<Task> StartListen()
{
_cts = new CancellationTokenSource();
_bgTask = BackgroundController4ContextHandlers(_cts.Token);
return ListenHttpAsync(_cts.Token);
}
private async Task BackgroundController4ContextHandlers(CancellationToken ct)
{
await Task.Run(async () =>
{
while (!ct.IsCancellationRequested)
{
var completedTask = await Task.WhenAny(_httpContextTasks);
_httpContextTasks.Remove(completedTask);
var res = completedTask.Result;
var strResult = res.ToString();
_logger.Information("{HttpServer}","ЗАПРОС ОБРАБОТАН", strResult);
}
_logger.Information("{HttpServer}","ФОНОВАЯ обработка Task запросов остановлена");
}, ct);
}
private async Task ListenHttpAsync(CancellationToken ct)
{
_listener.Start();
_logger.Information("{HttpServer}", "Ожидание запросов ...");
while (!ct.IsCancellationRequested)
{
try
{
var context = await _listener.GetContextAsync();
var handler = HttpListenerContextHandlerAsync(context, ct);
_httpContextTasks.Add(handler);
}
catch (TaskCanceledException) { }
}
}
Ответы (1 шт):
Если просто в лоб оптимизировать код, то у меня бы что-то такое получилось
public Result<Task> StartListen()
{
_cts = new CancellationTokenSource();
return ListenHttpAsync(_cts.Token);
}
private async Task ListenHttpAsync(CancellationToken ct)
{
_listener.Start();
_logger.Information("{HttpServer}", "Ожидание запросов ...");
try
{
while (true)
{
var context = await _listener.GetContextAsync();
var handler = HttpListenerContextHandlerAsync(context, ct);
handler.ContinueWith(t =>
{
if (t.IsCompletedSuccessfully)
{
var strResult = t.Result.ToString();
_logger.Information("{HttpServer}","ЗАПРОС ОБРАБОТАН", strResult);
}
else
{
var strError = t.Exception.Message;
_logger.Warning("{HttpServer}","ЗАПРОС НЕ ОБРАБОТАН", strError);
}
}, ct);
}
}
catch (OperationCanceledException ex)
{
_logger.Information("{HttpServer}","Конец ожидания запросов", ex.Message);
}
}
Единственный нюанс, я здесь вообще не ожидаю запущеную таску и нигде ее не храню. Поэтому внутри континуации обрабатываю исключение, но так же нужно учесть, чтобы внутри самой континуации все исключения были так же обработаны и она завершилась успешно. Такой подход прикольнее в плане производительности, нет коллекции, по которой надо постоянно бегать и искать, где же там таск завершенный остался. Таск просто запускает колбэк, когда успешно или не успешно завершается.