Выносится ли в отдельный поток BackgroundService asp.net core

Уже долгое время не могу найти ответ на свой вопрос. Выносится ли в отдельный поток BackgroundService? Заходил на форумы, увидел ответ, что фоновая задача помечается как LongRunning, и каждому фоновому сервису выделяется отдельный поток, но при эксперименте

class TestService(ILogger<s> loger) : BackgroundService
{
    private readonly ILogger<s> _loger = loger;
    protected override  Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _loger.LogInformation("Фоновая задача запущена");
        
        while (!stoppingToken.IsCancellationRequested)
        {
             
            _loger.LogInformation("Цикл");
        }
       
    }
}

все приложение зависает и не запускается Но если добавить в первой строчке await Task.Yield(), или другую строку, содержащая await с асинхронной операцией , то приложение благополучно запустится, а задача впоследствии продолжит выполняться. Что это значит. Фоновая задача не выполняется в отдельном потоке, а в главном? Или она выполняется в главном потоке до первого await а после переносится в отдельный поток?


Ответы (1 шт):

Автор решения: Uranus

Метод ExecuteAsync вызывается в том же потоке, который обрабатывает StartAsync. Это можно увидеть в коде BackgroundService.

public virtual Task StartAsync(CancellationToken cancellationToken)
{
    // Create linked token to allow cancelling executing task from provided token
    _stoppingCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

    // Store the task we're executing
    _executeTask = ExecuteAsync(_stoppingCts.Token);

    // If the task is completed then return it, this will bubble cancellation and failure to the caller
    if (_executeTask.IsCompleted)
    {
        return _executeTask;
    }

    // Otherwise it's running
    return Task.CompletedTask;
}

Фоновость реализуется не за счет выделенного потока, а за счет механизма TAP (Task-based Asynchronous Pattern). StartAsync не ждет завершения ExecuteAsync. Вместо этого он ожидает, что ExecuteAsync немедленно вернет Task, а его выполнение продолжится в фоне. Если внутри ExecuteAsync запустить бесконечный цикл без await, то он навсегда блокирует поток, в котором был запущен. Бесконечные циклы внутри BackgroundService должны быть асинхронными, чтобы не блокировать поток.

→ Ссылка