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

Асинхронные методы, async и await

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

Нередко программа выполняет такие операции, которые могут занять продолжительное время, например, обращение к сетевым ресурсам, чтение-запись файлов, обращение к базе данных и т.д. Такие операции могут серьезно нагрузить приложение. Особенно это актуально в графических (десктопных или мобильных) приложениях, где продолжительные операции могут блокировать интерфейс пользователя и негативно повлиять на желание пользователя работать с программой, или в веб-приложениях, которые должны быть готовы обслуживать тысячи запросов в секунду. В синхронном приложении при выполнении продолжительных операций в основном потоке этот поток просто бы блокировался на время выполнения операции. И чтобы продолжительные операции не блокировали общую работу приложения, в C# можно задействовать асинхронность.

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

Ключевыми для работы с асинхронными вызовами в C# являются два оператора: async и await, цель которых - упростить написание асинхронного кода. Они используются вместе для создания асинхронного метода.

Асинхронный метод обладает следующими признаками:

  • В заголовке метода используется модификатор async

  • Метод содержит одно или несколько выражений await

  • В качестве возвращаемого типа используется один из следующих:

    • void

    • Task

    • Task<T>

    • ValueTask<T>

Асинхронный метод, как и обычный, может использовать любое количество параметров или не использовать их вообще. Однако асинхронный метод не может определять параметры с модификаторами out, ref и in.

Также стоит отметить, что слово async, которое указывается в определении метода, НЕ делает автоматически метод асинхронным. Оно лишь указывает, что данный метод может содержать одно или несколько выражений await.

Рассмотрим простейший пример определения и вызова асинхронного метода:


await PrintAsync();   // вызов асинхронного метода
Console.WriteLine("Некоторые действия в методе Main");

void Print()
{
    Thread.Sleep(3000);     // имитация продолжительной работы
    Console.WriteLine("Hello METANIT.COM");
}

// определение асинхронного метода
async Task PrintAsync()
{
    Console.WriteLine("Начало метода PrintAsync"); // выполняется синхронно
    await Task.Run(Print);                // выполняется асинхронно
    Console.WriteLine("Конец метода PrintAsync");
}	

Здесь прежде всего определен обычный метод Print, который просто выводит некоторую строку на консоль. Для имитации долгой работы в нем используется задержка на 3 секунд с помощью метода Thread.Sleep(). То есть условно Print - это некоторый метод, который выполняет некоторую продолжительную операцию. В реальном приложении это могло бы быть обращение к базе данных или чтение-запись файлов, но для упрощения понимания он просто выводит строку на консоль.

Также здесь определен асинхронный метод PrintAsync(). Асинхронным он является потому, что имеет в определении перед возвращаемым типом модификатор async, его возвращаемым типом является Task, и в теле метода определено выражение await.

Стоит отметить, что явным образом метод PrintAsync не возвращает никакого объекта Task, однако поскольку в теле метода применяется выражение await, то в качестве возвращаемого типа можно использовать тип Task.

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

await Task.Run(Print);

По негласным правилам в названии асинхроннных методов принято использовать суффикс Async - PrintAsync(), хотя в принципе это необязательно делать.

И затем в программе (в данном случае в методе Main) вызывается этот асинхронный метод.

await PrintAsync();   // вызов асинхронного метода

Посмотрим, какой у программы будет консольный вывод:


Начало метода PrintAsync
Hello METANIT.COM
Конец метода PrintAsync
Некоторые действия в методе Main

Разберем поэтапно, что здесь происходит:

  1. Запускается программа, а точнее метод Main, в котором вызывается асинхронный метод PrintAsync.

  2. Метод PrintAsync начинает выполняться синхронно вплоть до выражения await.

    Console.WriteLine("Начало метода PrintAsync"); // выполняется синхронно
  3. Выражение await запускает асинхронную задачу Task.Run(()=>Print())

  4. Пока выполняется асинхронная задача Task.Run(()=>Print()) (а она может выполняться довольно продожительное время), выполнение кода возвращается в вызывающий метод - то есть в метод Main.

  5. Когда асинхронная задача завершила свое выполнение (в случае выше - вывела строку через три секунды), продолжает работу асинхронный метод PrintAsync, который вызвал асинхронную задачу.

  6. После завершения метода PrintAsync продолжает работу метод Main.

Асинхронный метод Main

Стоит учитывать, что оператор await можно применять только в методе, который имеет модификатор async. И если мы в методе Main используем оператор await, то метод Main тоже должен быть определен как асинхронный. То есть предыдущий пример фактически будет аналогичен следующему:


class Program
{
    async static Task Main(string[] args)
    {
        await PrintAsync();   // вызов асинхронного метода
        Console.WriteLine("Некоторые действия в методе Main");


        void Print()
        {
            Thread.Sleep(3000);     // имитация продолжительной работы
            Console.WriteLine("Hello METANIT.COM");
        }

        // определение асинхронного метода
        async Task PrintAsync()
        {
            Console.WriteLine("Начало метода PrintAsync"); // выполняется синхронно
            await Task.Run(Print);                // выполняется асинхронно
            Console.WriteLine("Конец метода PrintAsync");
        }
    }
}

Задержка асинхронной операции и Task.Delay

В асинхронных методах для остановки метода на некоторое время можно применять метод Task.Delay(). В качестве параметра он принимает количество миллисекунд в виде значения int, либо объект TimeSpan, который задает время задержки:


await PrintAsync();   // вызов асинхронного метода
Console.WriteLine("Некоторые действия в методе Main");

// определение асинхронного метода
async Task PrintAsync()
{
    await Task.Delay(3000);     // имитация продолжительной работы
    // или так
    //await Task.Delay(TimeSpan.FromMilliseconds(3000));
    Console.WriteLine("Hello METANIT.COM");
}	

Причем метод Task.Delay сам по себе представляет асинхронную операцию, поэтому к нему применяется оператор await.

Преимущества асинхронности

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


PrintName("Tom");
PrintName("Bob");
PrintName("Sam");

void PrintName(string name)
{
    Thread.Sleep(3000);     // имитация продолжительной работы
    Console.WriteLine(name);
}

Данный код является синхронным и выполняет последовательно три вызова метода PrintName. Поскольку для имитации продолжительной работы в методе установлена задержка на три секунды, то общее выполнение программы займет не менее 9 секунд. Так как каждый последующий вызов PrintName будет ждать пока завершится предыдущий.

Изменим в программе синхронный метод PrintName на асинхронный:


await PrintNameAsync("Tom");
await PrintNameAsync("Bob");
await PrintNameAsync("Sam");

// определение асинхронного метода
async Task PrintNameAsync(string name)
{
    await Task.Delay(3000);     // имитация продолжительной работы
    Console.WriteLine(name);
}

Вместо метода PrintName теперь вызывается три раза PrintNameAsync. Для имитации продолжительной работы в методе установлена задержка на 3 секунды с помощью вызова Task.Delay(3000). И поскольку при вызовае каждого метода применяется оператор await, который останавливает выполнение до завершения асинхронного метода, то общее выполнение программы опять же займет не менее 9 секунд. Тем не менее теперь выполнение асинхронных операций не блокирует основной поток.

Теперь оптимизируем программу:


var tomTask = PrintNameAsync("Tom");
var bobTask = PrintNameAsync("Bob");
var samTask = PrintNameAsync("Sam");

await tomTask;
await bobTask;
await samTask;
// определение асинхронного метода
async Task PrintNameAsync(string name)
{
    await Task.Delay(3000);     // имитация продолжительной работы
    Console.WriteLine(name);
}

В данном случае задачи фактически запускаются при определении. А оператор await применяется лишь тогда, когда нам нужно дождаться завершения асинхронных операций - то есть в конце программы. И в этом случае общее выполнение программы займет не менее 3 секунд, но гораздо меньше 9 секунд.

Определение асинхронного лямбда-выражения

Асинхронную операцию можно определить не только с помощью отдельного метода, но и с помощью лямбда-выражения:


// асинхронное лямбда-выражение
Func<string, Task> printer = async (message) =>
{
    await Task.Delay(1000);
    Console.WriteLine(message);
};

await printer("Hello World");
await printer("Hello METANIT.COM");

Проверь себя

📋 Тест по теме «Асинхронные методы, async и await» Тест по теме

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

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

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

_____ PrintAsync();   // вызов асинхронного метода
Console.WriteLine("Некоторые действия в методе Main");

void Print()
{
    Thread.Sleep(3000);     // имитация продолжительной работы
    Console.WriteLine("Hello METANIT.COM");
}

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

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

await _____.Run(Print);
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (3)
+10 XP

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

_____ PrintAsync();   // вызов асинхронного метода
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (4)
+10 XP

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

Начало метода PrintAsync
Hello METANIT._____
Конец метода PrintAsync
Некоторые действия в методе Main
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (5)
+10 XP

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

class Program
{
    async static Task Main(string[] _____)
    {
        await PrintAsync();   // вызов асинхронного метода
        Console.WriteLine("Некоторые действия в методе Main");


        void Print()
        {
            Thread.Sleep(3000);     // имитация продолжительной работы
            Console.WriteLine("Hello METANIT.COM");
        }

        // определение асинхронного метода
        async Task PrintAsync()
        {
            Console.WriteLine("Начало метода PrintAsync"); // выполняется синхронно
            await Task.Run(Print);                // выполняется асинхронно
            Console.WriteLine("Конец метода PrintAsync");
        }
    }
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (1)
+20 XP

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

await PrintAsync();   // вызов асинхронного метода
Console._____("Некоторые действия в методе Main");

void Print()
{
    Thread.Sleep(3000);     // имитация продолжительной работы
    Console.WriteLine("_____ METANIT.COM");
}

// определение асинхронного метода
async Task PrintAsync()
{
    Console.WriteLine("Начало метода PrintAsync"); // выполняется синхронно
    await Task.Run(Print);                // выполняется асинхронно
    Console.WriteLine("Конец метода PrintAsync");
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (2)
+20 XP

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

_____ _____.Run(Print);
Войдите, чтобы отправить решение
Среднее Ручная проверка Расставьте строки кода в правильном порядке
+20 XP

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

Конец метода PrintAsync
Некоторые действия в методе Main
Hello METANIT.COM
Начало метода PrintAsync
Войдите, чтобы отправить решение
Сложное Ручная проверка Объясните и модифицируйте пример из урока «Асинхронные методы, async и await»
+30 XP

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

await PrintAsync();   // вызов асинхронного метода
Console.WriteLine("Некоторые действия в методе Main");

void Print()
{
    Thread.Sleep(3000);     // имитация продолжительной работы
    Console.WriteLine("Hello METANIT.COM");
}

// определение асинхронного метода
async Task PrintAsync()
{
    Console.WriteLine("Начало метода PrintAsync"); // выполняется синхронно
    await Task.Run(Print);                // выполняется асинхронно
    Console.WriteLine("Конец метода PrintAsync");
}
Войдите, чтобы отправить решение
Сложное Ручная проверка Найдите потенциальную проблему в коде урока «Асинхронные методы, async и await»
+30 XP

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

await Task.Run(Print);
Войдите, чтобы отправить решение
Мини-проект Ручная проверка Мини-проект по теме «Асинхронные методы, async и await»
+50 XP

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

Войдите, чтобы отправить решение
Домашнее задание Ручная проверка Домашнее задание по теме «Асинхронные методы, async и await»
+25 XP

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

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

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

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