Зачем нам нужен новый подход ?

Structured concurrency (далее SC) позволяет рассуждать о конкуретном вычислении используя специальные точки, позволяет узнать о разветвлениях, конкурентных вычислениях и увидеть результат вычислений, подобно тому, как работает блок условия if else в синхронном коде.

Конкурентная задача начинается, когда вы используете async let, создаете открепленную (detached) задачу или группу задач. Задача возобновляются в точке приостановке (suspention point), обозначаемой await. Однако не все задачи являются структурированными.

important

Структурированные задачи создаются с помощью async let и групп задач, а не структурированные — используя Task и Task.detached.

Structured VS Unstructured

Структурированные задачи живут до конца области видимости, подобно локальным переменным и автоматически отменяются при выходе из области видимости. Такой подход даёт явно понять как долго задача должна жить.

note

Старайтесь, по возможности, использовать SC, вместо не структурированных задач (Unstructured).

О преимуществах SC вы можете прочитать ниже, а пока посмотрим на конкретный пример.

Представьте, что в нашем распоряжении кухня с несколькими шеф поварами, каждый из которых хочет приготовить суп. Приготовление супа состоит из нескольких шагов: нарезка ингридиентов, мариновка курицы, варка бульона и финальное приготовление. Некоторые задачи можно выполнить параллельно, а другие только в определенном порядке.

Soup

Посмотрим на нашу функцию приготовления makeSoup.

Make Soup UC

Возможно вы захотите явно добавить неструктурированные задачи Task { … } к вашим функциям и будете ожидать возвращения значения. Это подразумевает, что задача будет выполняться конкуретно, но все же это не рекомендуемый способ использовать конкурентность в Swift. Посмотрите на эту же функцию, но с использованием SC.

Make Soup SC

Поскольку мы знаем количество дочерних задач, мы можем использовать удобный синтаксис async let. В этом случае, задачи формируют структурированные отношения с родительской задачей. Чуть позже вы узнаете почему это важно.

makeSoup вызывает несколько асинхронных функций, одна из которых нарезка ингридиентов chopIngridients(…), которая принимает список ингридиентов и использует taskGroup { … } для конкрурентной нарезки.

Chop

Вы ознакомились с задачей по приготовлению супа. Самое время посмотреть как выглядит дерево иерархии (Task tree) этой задачи.

Task tree

Дочерние задачи нашей функции выделены цветными квадратами. Стрелки указывают на отношения между родительскими и дочерними задачами. Функция makeSoup содержит 3 дочерние задачи:

  1. Нарезка ингридиентов chopIngridients(🍍, 🥕, 🧅)
  2. Мариновка курицы marinate(🍗)
  3. Варка бульона boil broth

Функция chopIngridients(…) использует taskGroup { … } для создания дочерних задач для каждого ингридиента. Мы используем 3 ингридиента, поэтому будут созданы 3 дочерних задачи. Такие родитеские и дочерние отношения формируют дерево задач (task tree).

Про отмену задач сказано в другой главе.