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

Асинхронные стримы

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

Начиная с версии C# 8.0 в C# были добавлены асинхронные стримы, которые упрощают работу с потоками данных в асинхронном режиме. Хотя асинхронность в C# существует уже довольно давно, тем не менее асинхронные методы до сих пор позволяли получать один объект, когда асинхронная операция была готова предоставить результат. Для возвращения нескольких значений в C# могут применяться итераторы, но они имеют синхронную природу, блокируют вызывающий поток и не могут использоваться в асинхронном контексте. Асинхронные стримы обходят эту проблему, позволяя получать множество значений и возвращать их по мере готовности в асинхронном режиме.

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

  • метод имеет модификатор async

  • метод возвращает объект IAsyncEnumerable<T>. Интерфейс IAsyncEnumerable определяет метод GetAsyncEnumerator, который возвращает IAsyncEnumerator:

    public interface IAsyncEnumerable<out T>
    {
    	IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default);
    }
    
    public interface IAsyncEnumerator<out T> : IAsyncDisposable
    {
    	T Current { get; }
    	ValueTask<bool> MoveNextAsync();
    }
    public interface IAsyncDisposable
    {
        ValueTask DisposeAsync();
    }
    
  • метод содержит выражения yield return для последовательного получения элементов из асинхронного стрима

Фактически асинхронный стрим объединяет асинхронность и итераторы. Рассмотрим простейший пример:


await foreach (var number in GetNumbersAsync())
{
    Console.WriteLine(number);
}

async IAsyncEnumerable<int> GetNumbersAsync()
{
    for (int i = 0; i < 10; i++)
    {
        await Task.Delay(100);
        yield return i;
    }
}

Итак, метод GetNumbersAsync() фактически и представляет асинхронный стрим. Этот метод является асинхронным. Его возвращаемый тип - IAsyncEnumerable<int>. А его суть сводится к тому, что он возвращает с помощью yield return каждые 100 миллисекунд некоторое число. То есть фактически метод должен вернуть 10 чисел от 0 до 9 с промежутком в 100 миллисекунд.

Для получения данных из стрима в методе Main используется цикл foreach:

await foreach (var number in GetNumbersAsync())

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

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


class Repository
{
	string[] data = { "Tom", "Sam", "Kate", "Alice", "Bob" };
	public async IAsyncEnumerable<string> GetDataAsync()
	{
		for (int i = 0; i < data.Length; i++)
		{
			Console.WriteLine($"Получаем {i + 1} элемент");
			await Task.Delay(500);
			yield return data[i];
		}
	}
}

Для упрощения примера данные здесь представлены в виде простого внутреннего массива строк. Для имитации задержки в получении применяется метод Task.Delay.

Получим эти данные в программе:


Repository repo = new Repository();
IAsyncEnumerable<string> data = repo.GetDataAsync();
await foreach (var name in data)
{
    Console.WriteLine(name);
}

class Repository
{
    string[] data = { "Tom", "Sam", "Kate", "Alice", "Bob" };
    public async IAsyncEnumerable<string> GetDataAsync()
    {
        for (int i = 0; i < data.Length; i++)
        {
            Console.WriteLine($"Получаем {i + 1} элемент");
            await Task.Delay(500);
            yield return data[i];
        }
    }
}

Проверь себя

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

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

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

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

_____ foreach (var number in GetNumbersAsync())
{
    Console.WriteLine(number);
}

async IAsyncEnumerable<int> GetNumbersAsync()
{
    for (int i = 0; i < 10; i++)
    {
        await Task.Delay(100);
        yield return i;
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (2)
+10 XP

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

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

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

class Repository
{
	string[] data = { "_____", "Sam", "Kate", "Alice", "Bob" };
	public async IAsyncEnumerable<string> GetDataAsync()
	{
		for (int i = 0; i < data.Length; i++)
		{
			Console.WriteLine($"Получаем {i + 1} элемент");
			await Task.Delay(500);
			yield return data[i];
		}
	}
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (4)
+10 XP

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

_____ repo = new Repository();
IAsyncEnumerable<string> data = repo.GetDataAsync();
await foreach (var name in data)
{
    Console.WriteLine(name);
}

class Repository
{
    string[] data = { "Tom", "Sam", "Kate", "Alice", "Bob" };
    public async IAsyncEnumerable<string> GetDataAsync()
    {
        for (int i = 0; i < data.Length; i++)
        {
            Console.WriteLine($"Получаем {i + 1} элемент");
            await Task.Delay(500);
            yield return data[i];
        }
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (5)
+10 XP

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

await foreach (var number in GetNumbersAsync())
{
    Console.WriteLine(number);
}

_____ IAsyncEnumerable<int> GetNumbersAsync()
{
    for (int i = 0; i < 10; i++)
    {
        await Task.Delay(100);
        yield return i;
    }
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (1)
+20 XP

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

_____ foreach (var number in GetNumbersAsync())
{
    Console.WriteLine(number);
}

async IAsyncEnumerable<int> GetNumbersAsync()
{
    for (int i = _____; i < 10; i++)
    {
        await Task.Delay(100);
        yield return i;
    }
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (2)
+20 XP

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

await foreach (var _____ in _____())
Войдите, чтобы отправить решение
Сложное Ручная проверка Объясните и модифицируйте пример из урока «Асинхронные стримы»
+30 XP

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

await foreach (var number in GetNumbersAsync())
{
    Console.WriteLine(number);
}

async IAsyncEnumerable<int> GetNumbersAsync()
{
    for (int i = 0; i < 10; i++)
    {
        await Task.Delay(100);
        yield return i;
    }
}
Войдите, чтобы отправить решение
Сложное Ручная проверка Найдите потенциальную проблему в коде урока «Асинхронные стримы»
+30 XP

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

await foreach (var number in GetNumbersAsync())
Войдите, чтобы отправить решение
Мини-проект Ручная проверка Мини-проект по теме «Асинхронные стримы»
+50 XP

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

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

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

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

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

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