Обложка канала

.NET Разработчик

Опытный разработчик не так давно зашёл в .Net и поставил цель получить сертификат Microsoft. Свой ежедневный прогресс он описывает на канале .Net Разработчик. Заметки об изученном материале, советы по повышению производительности и поддержке мотивации, ин

.NET Разработчик

4 года назад
Открыть в
День 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.