TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.76K subscribers
Post #1380 1.52K
День 1117. #ЗаметкиНаПолях #AsyncTips
Динамический параллелизм

Задача
: Требуется реализовать параллельную обработку, когда структура и количество параллельных задач становятся известны только во время выполнения.

Решение
Класс Parallel и Parallel LINQ, которые рассматривались ранее, — всего лишь удобные обертки для мощного типа Task. Если требуется реализовать динамический параллелизм, проще использовать тип Task напрямую.
В примере ниже для каждого узла бинарного дерева необходимо выполнить некоторую затратную обработку. Структура дерева неизвестна до стадии выполнения. Метод Traverse обрабатывает текущий узел, а затем создаёт две дочерние задачи, по одной для каждой ветви (в данном примере предполагается, что родительские узлы должны быть обработаны до перехода к дочерним узлам):
void Traverse(Node current)
{
DoActionOnNode(current);
if (current.Left != null)
Task.Factory.StartNew(
() => Traverse(current.Left),
CancellationToken.None,
TaskCreationOptions.AttachedToParent,
TaskScheduler.Default);

if (current.Right != null)
Task.Factory.StartNew(
() => Traverse(current.Right),
CancellationToken.None,
TaskCreationOptions.AttachedToParent,
TaskScheduler.Default);
}

Метод ProcessTree начинает обработку, создавая родительскую задачу верхнего уровня и ожидая её завершения:
void ProcessTree(Node root)
{
Task task = Task.Factory.StartNew(
() => Traverse(root),
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default);

task.Wait();
}

Флаг AttachedToParent гарантирует, что задача для каждой ветви связывается с задачей своего родительского узла. Таким образом создаются отношения «родитель/потомок» между экземплярами Task. Родительские задачи выполняют своего делегата, после чего ожидают завершения своих дочерних задач. Исключения от дочерних задач распространяются к родительской задаче. Таким образом, ProcessTree может ожидать задач для всего дерева, для чего достаточно вызвать Wait для одной задачи в корне.

Другой способ решения этой задачи - сохранить каждую новую задачу в потокобезопасной коллекции, а затем ожидать завершения их всех с использованием Task.WaitAll.

Если ваша ситуация не относится к категории «родитель/потомок», вы можете запланировать запуск любой задачи после другой задачи, используя продолжение (continuation). Оно представляет собой отдельную задачу, которая выполняется после завершения исходной:
Task task = Task.Factory.StartNew(
() => Thread.Sleep(TimeSpan.FromSeconds(2)),
CancellationToken.None,
TaskCreationOptions.None,
TaskScheduler.Default);

Task continuation = task.ContinueWith(
t => Trace.WriteLine("Task is done"),
CancellationToken.None,
TaskContinuationOptions.None,
TaskScheduler.Default);

Замечание: всегда лучше явно задавать планировщик TaskScheduler, используемый в StartNew и ContinueWith.

Использование Task для параллельной и асинхронной обработки принципиально отличаются:
1. Параллельные задачи:
- могут использовать блокирующие методы, такие как Task.Wait, Task.Result, Task.WaitAll и Task.WaitAny,
- обычно используют AttachedToParent для создания отношений «родитель/потомок» между задачами,
- следует создавать методами Task.Run или Task.Factory.StartNew.

2. Асинхронные задачи:
- следует использовать с await, Task.WhenAll и Task.WhenAny вместо блокирующих методов,
- не должны использовать AttachedToParent, но могут формировать неявные отношения «родитель/потомок», используя ожидание других задач.

Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 4.
  • 👍 11
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 →