Как использовать IAsyncEnumerable в C#

автор vadim


IAsyncEnumerable — это мощная функция, представленная в C# 8.0, которая позволяет нам работать с последовательностью данных асинхронно. Как следует из названия, IAsyncEnumerable — это асинхронный аналог IEnumerable, интерфейса, который позволяет нам легко перебирать элементы коллекции.

Интересно, что интерфейс IAsyncEnumerable работает по принципу извлечения, при котором следующий элемент будет либо создан, либо получен по запросу потребителя. В отличие от IEnumerable, который ожидает создания следующего элемента, IAsyncEnumerable возвращает ожидаемый объект, который можно возобновить позже.

В этой статье мы рассмотрим, как можно использовать интерфейс IAsyncEnumerable в C# для работы с асинхронными потоками данных.

Создайте проект консольного приложения в Visual Studio.

Прежде всего давайте создадим проект консольного приложения .NET Core в Visual Studio. Предполагая, что в вашей системе установлена ​​Visual Studio 2022, выполните действия, описанные ниже, чтобы создать новый проект консольного приложения .NET Core в Visual Studio.

  1. Запустите интегрированную среду разработки Visual Studio.
  2. Нажмите «Создать новый проект».
  3. В окне «Создать новый проект» выберите «Консольное приложение (.NET Core)» из списка отображаемых шаблонов.
  4. Нажмите “Далее.
  5. В окне «Настроить новый проект» укажите имя и местоположение нового проекта.
  6. Нажмите “Далее.
  7. В показанное далее окно «Дополнительная информация» выберите «.NET 7.0 (стандартный срок поддержки)» в качестве версии Framework, которую вы хотели бы использовать.
  8. Нажмите Создать.

Мы будем использовать этот проект консольного приложения .NET 7 для работы с примерами кода, показанными в последующих разделах этой статьи.

Преимущества IAsyncEnumerable

Ключевые преимущества IAsyncEnumerable включают следующее:

  • Поддержка асинхронной потоковой передачи: Традиционная коллекция, такая как список или IEnumerable, требует, чтобы все элементы были доступны заранее. IAsyncEnumerable, с другой стороны, передает элементы по мере их появления. Использование IAsyncEnumerable особенно полезно при работе с большими наборами данных или потоками данных в реальном времени, где все данные могут быть недоступны заранее. С помощью IAsyncEnumerable вы можете начать обработку элементов немедленно, не дожидаясь полной загрузки набора данных. Такая гибкость полезна при работе с потоками данных в режиме реального времени, такими как котировки акций или обновления в социальных сетях, где новая информация генерируется постоянно и требует обработки, как только она становится доступной.
  • Эффективное использование имеющихся ресурсов: IAsyncEnumerable позволяет работать с большими последовательностями данных в асинхронном режиме, что гарантирует, что ценные вычислительные ресурсы не будут потрачены зря.
  • Повышенная производительность: IAsyncEnumerable может повысить производительность вашего приложения, устраняя необходимость загружать все данные за один раз. Следовательно, система может использовать меньше памяти и освободить ресурсы.
  • Улучшенная отзывчивость: С помощью IAsyncEnumerable вы можете легко написать код, который оперативно обрабатывает большие потоки данных. Вы также можете более эффективно использовать ресурсы и повысить общую производительность приложения, чтобы ваше приложение оставалось отзывчивым даже при работе с большими наборами данных.
  • Более простой код: IAsyncEnumerable упрощает код, устраняя сложные механизмы синхронизации, такие как блокировки и семафоры, тем самым снижая вероятность взаимоблокировок и других проблем, связанных с синхронизацией, в вашем приложении.

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

Фильтрация с помощью IAsyncEnumerable

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

public async IAsyncEnumerable<int> GetEvenNumbersAsync(IEnumerable<int> integers)
{
    foreach (var n in integers)
    {
        await Task.Delay(500);
        if (n % 2 == 0)
        {
            yield return n;
        }
    }
}

Агрегация с помощью IAsyncEnumerable

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

public async Task<int> GetSumAsync(IEnumerable<int> integers)
{
    int sum = 0;
        await foreach (var n in GetNumbersAsync(integers))
    {
        sum += n;
    }
        return sum;
}

Асинхронная проекция с IAsyncEnumerable

В следующем фрагменте кода показано, как можно использовать проекции с IAsyncEnumerable.

public async IAsyncEnumerable<int> GetNumbersUsingProjectionAsync(IEnumerable<int> integers)
{
    foreach (var n in integers)
    {
        await Task.Delay(500);
        yield return await Task.Run(() => n * 10);
    }
}

Преобразование последовательности с помощью IAsyncEnumerable

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

public async IAsyncEnumerable<string> TransformNumbersAsync(IEnumerable<int> integers)
{
    foreach (var n in integers)
    {
        await Task.Delay(500);
        yield return n.ToString();
    }
}

Пакетная обработка с помощью IAsyncEnumerable

Помимо выборки элементов по одному, вы также можете обрабатывать элементы пакетно при работе с IAsyncEnumerable. Это показано в фрагменте кода, приведенном ниже.

public static async IAsyncEnumerable<IEnumerable<T>> ProcessBatch<T>(IAsyncEnumerable<T> source, int size,
    CancellationToken cancellationToken = default)
{
    var batch = new List<T>();
    await foreach (var item in source)
    {
        if (cancellationToken.IsCancellationRequested)
            yield break;
        batch.Add(item);
        if (batch.Count >= size)
        {
            yield return batch;
            batch = new List<T>();
        }
    }
    if (batch.Count > 0)
    {
        yield return batch;
    }
}

Буферизация с помощью IAsyncEnumerable

Вы также можете воспользоваться преимуществами буферизации для повышения производительности при асинхронной обработке элементов.

public static async IAsyncEnumerable<T> ProcessBuffer<T>(this IAsyncEnumerable<T> source, int size,
    CancellationToken cancellationToken = default)
    {
        var queue = new Queue<T>();
        await foreach (var item in source)
        {
            if (cancellationToken.IsCancellationRequested)
                yield break;
            queue.Enqueue(item);
            while (queue.Count >= size)
            {
                yield return queue.Dequeue();
            }
        }
        while (queue.Count > 0)
        {
            yield return queue.Dequeue();
        }
    }

Заключение

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

Дальше читайте это:

  • Облачные вычисления больше не являются пустяком
  • Что такое генеративный ИИ? Искусственный интеллект, который создает
  • Программирование с помощью ИИ: советы и лучшие практики от разработчиков
  • Python пытается удалить GIL и повысить параллелизм
  • 7 причин, по которым Java по-прежнему хороша
  • Война за лицензирование открытого исходного кода окончена

Related Posts

Оставить комментарий