在C#8.0中使用异步流

利用C#8.0中异步创建和使用数据流的功能来提高应用程序的性能

如何在C#8.0中使用异步流

异步编程已经存在了很长一段时间。.NET中引入了async和await关键字,使我们能够编写可以轻松利用异步优势的程序。但是,直到IAsyncEnumerable <T>在C#8.0中出现之前,没有任何方法可以异步使用数据流。

IAsyncEnumerable <T>与用于迭代集合的IEnumerable <T>方法相似,不同之处在于IAsyncEnumerable <T>允许我们异步遍历该集合。换句话说,IAsyncEnumerable <T>允许我们在不阻塞线程的情况下等待集合中的下一个元素。

在C#8.0中的IAsyncDisposableIAsyncEnumerable <T>和IAsyncEnumerator <T>

异步流使您可以异步使用数据流。在.NET Standard 2.1发行版中引入了接口IAsyncDisposable,IAsyncEnumerable <T>和IAsyncEnumerator <T>。这些接口使我们能够使用异步流。以下代码片段显示了这三个接口的代码。

public interface IAsyncDisposable
{
    ValueTask DisposeAsync();
}
public interface IAsyncEnumerable<out T>
{
    IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken
    token = default);
}
public interface IAsyncEnumerator<out T> : IAsyncDisposable
{
    ValueTask<bool> MoveNextAsync();
    T Current { get; }
}

为什么要使用异步流? 

想象一下,您有一个数据访问库,该库从数据存储中读取数据,并一次性发送所有结果。您可以轻松实现这种方法。您需要做的只是进行一些异步调用,以从基础数据存储中获取数据,然后立即返回所有数据。

只要您不需要在页面中返回数据,此解决方案就可以了。在这种情况下,您可能必须进行多次调用才能返回数据。在这方面,最好的解决方案是您可以在数据可用后立即将其发送回调用方。

这正是异步流可以拯救的地方。如果返回数据的方法是同步的,则可以将yield return语句与IEnumerable <T>一起使用。但是,此解决方案无法扩展,因为它将导致阻塞调用。

最好的解决方案是在返回IAsyncEnumerable <T>的异步方法中使用yield return。返回异步流的方法将返回IAsyncEnumerable <T>的实例,并包含一个或多个yield return语句。

在C#8.0中创建异步流

以下代码段说明了一个异步方法,该方法返回Task <IEnumerable <T >>。

class Program
    {
        const int DELAY = 1000;
        const int MIN = 1;
        const int MAX = 10;
        static async Task Main(string[] args)
        {
            foreach (int number in await GetData())
            {
                Console.WriteLine(number);
            }
            Console.ReadLine();
        }
        static async Task<IEnumerable<int>> GetData()
        {
            List<int> integers = new List<int>();
            for (int i = MIN; i <= MAX; i++)
            {
                await Task.Delay(DELAY);
                integers.Add(i);
            }
            return integers;
        }
    }

当您执行此应用程序时,它将等待10秒钟,然后在控制台窗口中显示1到10之间的所有数字。尽管GetData方法是异步的,但是此代码将一次返回所有这些数字,而不是一一生成就一一返回。

这就是yield关键字的用处。yield关键字(在C#2.0中引入)可以执行有状态迭代,并逐个返回集合的每个元素。您无需在返回数据之前创建一个临时集合来存储数据。

以下代码段显示了如何修改GetData方法以合并yield关键字。

static async IAsyncEnumerable<int> GetData()
{
   for (int i = MIN; i < MAX; i++)
   {
      yield return i;
      await Task.Delay(DELAY);  
   }
}

在C#8.0中使用异步流 

使用异步流时,需要指定await关键字,后跟foreach关键字。您可以从Main方法调用上述示例中的方法,如下面的代码片段所示。

static async Task Main(string[] args)
{
    await foreach (int number in GetData())
    {
        Console.WriteLine(number);
    }
  Console.ReadLine();
}

这是完整的程序供您参考。

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Test
{
    class Program
    {
        const int DELAY = 1000;
        const int MIN = 1;
        const int MAX = 10;
        static async Task Main(string[] args)
        {
            await foreach (int number in GetData())
            {
                Console.WriteLine(number);
            }
            Console.ReadLine();
        }
        static async IAsyncEnumerable<int> GetData()
        {
            for (int i = MIN; i < MAX; i++)
            {
                yield return i;
                await Task.Delay(DELAY);  
            }
        }
    }
}

IAsyncEnumerable <T>(又名异步流)的支持是C#8.0中最重要的新功能之一。您可以在应用程序中利用异步流来使代码更整洁,更高效和高性能。若要使用C#8.0编译器,您将需要Visual Studio 2019版本16.3或.NET Core 3.0 SDK。

原创文章,作者:冰封一夏,如若转载,请注明出处:http://www.nncjzx.com/3428.html

关注本站公众号获取更多实时内容

本站微信公众号:二线码农