Можно ли запустить фоновую задачу через Task.Run и сразу вернуть ответ из контроллера ASP.NET Core?

«Можно ли запустить фоновую задачу через Task.Run и сразу вернуть ответ из контроллера ASP.NET Core?» — вопрос из категории ASP.NET Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Технически — да, но делать этого категорически не рекомендуется для рабочих приложений.

Проблемный пример (антипаттерн):

[HttpPost("start-task")]
public IActionResult StartTask()
{
    // ПЛОХО: Приложение ничего не знает об этой задаче.
    Task.Run(() => 
    {
        Thread.Sleep(5000); // Имитация долгой работы
        _logger.LogInformation("Task completed");
    });
    return Ok("Задача запущена");
}

Риски такого подхода:

  • Потеря задачи: При рестарте или остановке приложения (деплой, масштабирование) задача будет безвозвратно потеряна.
  • Отсутствие отслеживания: Нет возможности узнать статус выполнения, обработать ошибки или отменить задачу.
  • Проблемы с DI: Фоновая задача, захватившая Scoped-сервис из контроллера, может использовать его после завершения HTTP-запроса, что приведет к ObjectDisposedException.

Правильные решения в ASP.NET Core:

  1. IHostedService / BackgroundService (для долгоживущих задач):

    public class TimedBackgroundService : BackgroundService
    {
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                await Task.Delay(1000, stoppingToken); // Работа каждую секунду
            }
        }
    }
    // Регистрация: services.AddHostedService<TimedBackgroundService>();
  2. Queued Hosted Service (для фоновых задач по запросу): Создайте безопасную очередь (Channel<T> или BlockingCollection<T>), куда контроллер будет помещать работу, а фоновая служба — выполнять.

  3. Специализированные библиотеки: Для сложных сценариев (повторные попытки, планирование, панель мониторинга) используйте Hangfire или Quartz.NET. Они надежно хранят состояние задач и обеспечивают их выполнение.

Вывод: Task.Run в контроллере — это "fire and forget", что небезопасно. Используйте встроенные механизмы фоновых служб или проверенные сторонние решения.