TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #2530 2.49K
День 2091. #ЗаметкиНаПолях
Task.Factory.StartNew и Длительные Асинхронные Задачи
Допустим, вы хотите реализовать шаблон «производитель-потребитель» на основе каналов для асинхронной обработки элементов:
public class Processor
{
private Channel<string> _channel =
Channel.CreateUnbounded<string>();
private Task _task;

public Processor()
{
_task = Task.Factory.StartNew(async () =>
{
await foreach (var log in
_channel.Reader.ReadAllAsync())
{
// Обработка
Console.WriteLine(log);
}
}, TaskCreationOptions.LongRunning);
}

public void ProcessLog(string log)
{
_channel.Writer.TryWrite(log);
}
}

И поскольку вы знаете, что задача обработки должна выполняться в течение всего процесса, вы используете флаг TaskCreationOptions.LongRunning.

Как думаете, есть здесь проблема? На самом деле, да.

Флаг LongRunning сообщает TPL о необходимости иметь выделенный поток для данного обратного вызова вместо получения потока из пула потоков. Но даже если ваша задача семантически долго выполняется, выделенный поток не будет выполняться долго.

Давайте упростим код и добавим немного трассировки:
static void WriteLine(string msg)
{
Console.WriteLine(
$"""
[{Environment.CurrentManagedThreadId}]
[IsThreadPoolThread: {Thread.CurrentThread.IsThreadPoolThread}]
{msg}.
"""
);
}

var task = Task.Factory.StartNew(async () =>
{
WriteLine("Task started");
await Task.Delay(1000);
WriteLine("Task completed");
}, TaskCreationOptions.LongRunning);

Thread.Sleep(100);
Console.WriteLine(
$"Completed: " + task.IsCompleted);

Console.ReadLine();

Вывод:
[4]
[IsThreadPoolThread: False]
Task started.
Completed: True
[6]
[IsThreadPoolThread: True]
Task completed.

Вот что происходит во время выполнения:
- Task started печатается из выделенного потока.
- Task completed печатается из потока пула потоков.

Задача, по-видимому, завершена до выполнения обратного вызова. Это происходит, потому что Task.Factory.StartNew не является асинхронно-дружественным. Фактический тип переменной задачи — Task<Task>, а родительская задача завершается, когда новый поток начинает выполнять обратный вызов, а не когда завершается сам обратный вызов.

Флаг LongRunning учитывается для запуска первого блока асинхронного метода перед первым await. Await приостанавливает выполнение асинхронного метода, а остальная часть метода планируется в потоке пула потоков планировщиком задач по умолчанию.

Теоретически возможно, что обратный вызов, который вы предоставляете StartNew, имеет длительную часть перед первым await, и вы действительно хотите запустить его в выделенном потоке. Если это так, флаг LongRunning допустим, но это очень редкое явление.

Рекомендации для Task.Factory.StartNew и асинхронных делегатов:
1. Избегайте использования Task.Factory.StartNew с асинхронными делегатами. Если необходимо, используйте метод расширения Unwarp, чтобы получить фактическую базовую задачу.
2. Не используйте флаг LongRunning с Task.Factory.StartNew для асинхронных обратных вызовов. Флаг полезен для синхронных методов, которые блокируют поток, но не для асинхронных методов, где продолжение будет запланировано в пуле потоков.

Источник: https://sergeyteplyakov.github.io/Blog/csharp/2024/10/01/Task.Factory.StartNew-and-long-running-async-tasks.html
  • 👍 24
More from @netdeveloperdiary
  1. Oct 6, 2026🦈 Открытое собеседование на Middle C# | 6 октября, 19:00 МСК Приглашаем на открытое собес…
  2. Oct 6, 2026День 2806. #ЗаметкиНаПолях Типы Коллекций в .NET, Которые Стоит Попробовать. Начало Больши…
  3. Oct 5, 2026День 2805. #ЧтоНовенького #NET11 Аргументы в Выражениях Коллекций в C#15 В C#15 реализован…
  4. Oct 4, 2026День 2804. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
  5. Oct 3, 2026Post #3360
  6. Oct 3, 2026День 2803. #Оффтоп Чем Заняться, Пока Работают Агенты? У VS Code Есть Ответ Сейчас большую…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →