TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.76K subscribers
Post #1738 1.44K
День 1406. #ЗаметкиНаПолях #AsyncTips
Планирование работы в пуле потоков

Любой блок кода должен выполняться в каком-то потоке. Планировщик (scheduler) решает, где должен выполняться тот или иной код. Обычно планировщик по умолчанию работает как надо. Например, оператор await в асинхронном коде автоматически возобновит выполнение метода в том же контексте, если только вы не переопределите это поведение.

Задача: имеется фрагмент кода, который должен выполняться в потоке из пула потоков.

Решение
В большинстве случаев следует использовать Task.Run. Следующий пример блокирует поток из пула на 2 секунды:
Task task = Task.Run(() =>
{
Thread.Sleep(2000);
});

Task.Run также поддерживает возвращаемые значения и асинхронные лямбда-выражения. Задача, возвращаемая Task.Run в следующем коде, завершится через 2 секунды с результатом 13:
Task<int> task = Task.Run(async () =>
{
await Task.Delay(2000);
return 13;
});

Task.Run возвращает объект Task (или Task<T>), который может естественным образом потребляться асинхронным или реактивным кодом.

Task.Run идеально подходит для UI-приложений с продолжительной работой, которая не должна выполняться в UI-потоке. Тем не менее не используйте Task.Run в ASP.NET, если только вы не уверены в том, что делаете. В ASP.NET код обработки запросов уже выполняется в потоке из пула, так что перенесение его в другой поток из пула обычно нерационально.

Task.Run является фактической заменой для BackgroundWorker, Delegate.BeginInvoke и ThreadPool.QueueUserWorkItem. Ни один из этих старых API не следует использовать в новом коде; код с Task.Run намного проще писать и сопровождать.

Task.Run справляется с большинством задач, для которых используется Thread, так что в большинстве случаев Thread может заменяться на Task.Run (за редким исключением).

Параллельный код и код потоков данных выполняется в пуле потоков по умолчанию, поэтому обычно Task.Run не нужно использовать с кодом, выполняемым через Parallel, библиотекой TPL Dataflow или Parallel LINQ.
Если вы применяете динамический параллелизм, используйте Task.Factory.StartNew вместо Task.Run. Это необходимо из-за того, что у объекта Task, возвращаемого Task.Run, параметры по умолчанию настроены для асинхронного использования (т.е. для потребления в асинхронном или реактивном коде). Кроме того, он не поддерживает такие расширенные возможности, как задачи «родитель/потомок», типичные для динамического параллельного кода.

Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 13.
  • 👍 19
More from @netdeveloperdiary
  1. Oct 13, 2026День 2813. #ЗаметкиНаПолях #Async Ещё Раз Про Async/Await в C#. Часть 1 1. Почему существу…
  2. Oct 12, 2026День 2812. #Карьера #AI Как Разработчикам Оставаться Полезными, Когда Код Пишет ИИ? По мер…
  3. Oct 11, 2026День 2811. #ВопросыНаСобеседовании Марк Прайс предложил свой набор из 60 вопросов (как тех…
  4. Oct 10, 2026День 2810. #ЧтоНовенького #VSCode Более Быстрый и Лёгкий C# Dev Kit Мы, разработчики, люби…
  5. Oct 9, 2026День 2809. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Окончание Начало 3.…
  6. Oct 8, 2026День 2808. #Карьера 5 Навыков, Которые Помогут Быстрее Стать Сеньором. Начало В ИТ есть се…
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 →