Полное руководство по языку программирования С# 14 и платформе .NET 10 / Aсинхронное программирование

Последовательное и параллельное выполнение. Task.WhenAll и Task.WhenAny

Начальный ⏱ 4 мин чтения 👁 2 🕓 обновлено 11.08.2026

Асинхронный метод может содержать множество выражений await. Когда система встречает в блоке кода оператор await, то выполнение в асинхронном методе останавливается, пока не завершится асинхронная задача. После завершения задачи управление переходит к следующему оператору await и так далее. Это позволяет вызывать асинхронные задачи последовательно в определенном порядке. Например:


await PrintAsync("Hello C#");
await PrintAsync("Hello World");
await PrintAsync("Hello METANIT.COM");

async Task PrintAsync(string message)
{
    await Task.Delay(2000);     // имитация продолжительной операции
    Console.WriteLine(message);
}

Консольный вывод данной программы:

Hello C#
Hello World
Hello METANIT.COM

То есть мы видим, что вызовы PrintAsync выполняются последовательно в том порядке, в котором они определены в коде. Каждая задача выполняется как минимум 2 секунды, соответственно общее время выполнения трех задач будет как минимум 6 секунд. И в данном случае вывод строго детерминирован.

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

Однако это не всегда необходимо. В подобном случае мы можем сразу запустить все задачи параллельно и применить оператор await там, где необходимо гарантировать завершение выполнения задачи, например, в самом конце программы.


// определяем и запускаем задачи
var task1 = PrintAsync("Hello C#");
var task2 = PrintAsync("Hello World");
var task3 = PrintAsync("Hello METANIT.COM");

// ожидаем завершения задач
await task1;
await task2;
await task3;

async Task PrintAsync(string message)
{
    await Task.Delay(2000);     // имитация продолжительной операции
    Console.WriteLine(message);
}

В этом случае все задачи запускаются и выполняются параллельно, соответственно общее время выполнения будет меньше 6 секунд, а консольный вывод программы недетерминирован. Например, он может быть следующим:

Hello METANIT.COM
Hello C#
Hello World

Однако .NET позволяет упростить отслеживание выполнения набора задач с помощью метода Task.WhenAll. Этот метод принимает набор асинхронных задач и ожидает завершения всех этих задач. Этот метод является аналогом статического метода Task.WaitAll(), однако предназначен непосредственно для асинхронных методов и позволяет применять оператор await:


// определяем и запускаем задачи
var task1 = PrintAsync("Hello C#");
var task2 = PrintAsync("Hello World");
var task3 = PrintAsync("Hello METANIT.COM");

// ожидаем завершения всех задач
await Task.WhenAll(task1, task2, task3);

async Task PrintAsync(string message)
{
    await Task.Delay(2000);     // имитация продолжительной операции
    Console.WriteLine(message);
}

Вначале запускаются три задачи. Затем Task.WhenAll создает новую задачу, которая будет автоматически выполнена после выполнения всех предоставленных задач, то есть задач task1, task2, task3. А с помощью оператора await ожидаем ее завершения.

Если нам надо дождаться, когда будет выполнена хотя бы одна задача из некоторого набора задач, то можно применять метод Task.WhenAny(). Это аналог метода Task.WaitAny() - он завершает выполнение, когда завершается хотя бы одна задача. Но для ожидания выполнения к Task.WhenAny() применяется оператор await:


// определяем и запускаем задачи
var task1 = PrintAsync("Hello C#");
var task2 = PrintAsync("Hello World");
var task3 = PrintAsync("Hello METANIT.COM");

// ожидаем завершения хотя бы одной задачи
await Task.WhenAny(task1, task2, task3);

async Task PrintAsync(string message)
{   
    await Task.Delay(new Random().Next(1000, 2000));     // имитация продолжительной операции
    Console.WriteLine(message);
}

Получение результата

Задачи, передаваемые в Task.WhenAll и Task.WhenAny, могут возвращать некоторое значение. В этом случае из методов Task.WhenAll и Task.WhenAny можно получить массив, который будет содержать результаты задач:


// определяем и запускаем задачи
var task1 = SquareAsync(4);
var task2 = SquareAsync(5);
var task3 = SquareAsync(6);

// ожидаем завершения всех задач
int[] results = await Task.WhenAll(task1, task2, task3);
// получаем результаты:
foreach (int result in results)
    Console.WriteLine(result);

async Task<int> SquareAsync(int n)
{
    await Task.Delay(1000);
    return n * n;
}

В данном случае метод Square возвращает число int - квадрат передаваемого в метод числа. И переменная results будет содержать результат вызова Task.WhenAll - по сути результаты всех трех запущенных задач. Поскольку все передаваемые в Task.WhenAll задачи возвращают int, то соответственно результат Task.WhenAll будет представлять массив значений int.

Также после завершения задачи ее результат можно получить стандартным образом через свойство Result:


// определяем и запускаем задачи
var task1 = SquareAsync(4);
var task2 = SquareAsync(5);
var task3 = SquareAsync(6);

await Task.WhenAll(task1, task2, task3);
// получаем результат задачи task2
Console.WriteLine($"task2 result: {task2.Result}"); // task2 result: 25

async Task<int> SquareAsync(int n)
{
    await Task.Delay(1000);
    return n * n;
}

Проверь себя

📋 Тест по теме «Последовательное и параллельное выполнение. Task.WhenAll и Task.WhenAny» Тест по теме

Практические задания

Лёгкое Ручная проверка Заполните пропуск в коде (1)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

_____ PrintAsync("Hello C#");
await PrintAsync("Hello World");
await PrintAsync("Hello METANIT.COM");

async Task PrintAsync(string message)
{
    await Task.Delay(2000);     // имитация продолжительной операции
    Console.WriteLine(message);
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (2)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

// определяем и запускаем задачи
var task1 = _____("Hello C#");
var task2 = PrintAsync("Hello World");
var task3 = PrintAsync("Hello METANIT.COM");

// ожидаем завершения задач
await task1;
await task2;
await task3;

async Task PrintAsync(string message)
{
    await Task.Delay(2000);     // имитация продолжительной операции
    Console.WriteLine(message);
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (3)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

// определяем и запускаем задачи
var task1 = PrintAsync("_____ C#");
var task2 = PrintAsync("Hello World");
var task3 = PrintAsync("Hello METANIT.COM");

// ожидаем завершения всех задач
await Task.WhenAll(task1, task2, task3);

async Task PrintAsync(string message)
{
    await Task.Delay(2000);     // имитация продолжительной операции
    Console.WriteLine(message);
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (4)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

// определяем и запускаем задачи
var task1 = PrintAsync("Hello C#");
var _____ = PrintAsync("Hello World");
var task3 = PrintAsync("Hello METANIT.COM");

// ожидаем завершения хотя бы одной задачи
await Task.WhenAny(task1, task2, task3);

async Task PrintAsync(string message)
{   
    await Task.Delay(new Random().Next(1000, 2000));     // имитация продолжительной операции
    Console.WriteLine(message);
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (5)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

// определяем и запускаем задачи
var task1 = _____(4);
var task2 = SquareAsync(5);
var task3 = SquareAsync(6);

// ожидаем завершения всех задач
int[] results = await Task.WhenAll(task1, task2, task3);
// получаем результаты:
foreach (int result in results)
    Console.WriteLine(result);

async Task<int> SquareAsync(int n)
{
    await Task.Delay(1000);
    return n * n;
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (1)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

await PrintAsync("Hello C#");
await PrintAsync("Hello World");
await PrintAsync("Hello _____.COM");

async _____ PrintAsync(string message)
{
    await Task.Delay(2000);     // имитация продолжительной операции
    Console.WriteLine(message);
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (2)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

// определяем и запускаем задачи
var task1 = PrintAsync("Hello C#");
var _____ = PrintAsync("Hello World");
var task3 = PrintAsync("Hello METANIT.COM");

// ожидаем завершения задач
_____ task1;
await task2;
await task3;

async Task PrintAsync(string message)
{
    await Task.Delay(2000);     // имитация продолжительной операции
    Console.WriteLine(message);
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Расставьте строки кода в правильном порядке
+20 XP

Строки кода из примера этого урока перемешаны. Впишите их в правильном порядке, каждую строку — с новой строки.

await PrintAsync("Hello World");
async Task PrintAsync(string message)
await PrintAsync("Hello C#");
    await Task.Delay(2000);     // имитация продолжительной операции
await PrintAsync("Hello METANIT.COM");
}
{
    Console.WriteLine(message);
Войдите, чтобы отправить решение
Сложное Ручная проверка Объясните и модифицируйте пример из урока «Последовательное и параллельное выполнение. Task.WhenAll и Task.WhenAny»
+30 XP

Опишите своими словами, что делает приведённый пример кода из урока «Последовательное и параллельное выполнение. Task.WhenAll и Task.WhenAny», и предложите одно осмысленное изменение (например, другое входное значение или дополнительное условие). Приведите итоговый код.

await PrintAsync("Hello C#");
await PrintAsync("Hello World");
await PrintAsync("Hello METANIT.COM");

async Task PrintAsync(string message)
{
    await Task.Delay(2000);     // имитация продолжительной операции
    Console.WriteLine(message);
}
Войдите, чтобы отправить решение
Сложное Ручная проверка Найдите потенциальную проблему в коде урока «Последовательное и параллельное выполнение. Task.WhenAll и T»
+30 XP

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

// определяем и запускаем задачи
var task1 = PrintAsync("Hello C#");
var task2 = PrintAsync("Hello World");
var task3 = PrintAsync("Hello METANIT.COM");

// ожидаем завершения задач
await task1;
await task2;
await task3;

async Task PrintAsync(string message)
{
    await Task.Delay(2000);     // имитация продолжительной операции
    Console.WriteLine(message);
}
Войдите, чтобы отправить решение
Мини-проект Ручная проверка Мини-проект по теме «Последовательное и параллельное выполнение. Task.WhenAll и Task.WhenAn»
+50 XP

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

Войдите, чтобы отправить решение
Домашнее задание Ручная проверка Домашнее задание по теме «Последовательное и параллельное выполнение. Task.WhenAll и Task.WhenAny»
+25 XP

Напишите собственную небольшую программу, применяющую материал темы «Последовательное и параллельное выполнение. Task.WhenAll и Task.WhenAny». Используйте не менее одной конструкции, показанной в примерах урока. Прикрепите код решения и краткое описание того, что он делает.

Войдите, чтобы отправить решение

Обсуждение (0)

Войдите, чтобы оставить комментарий.