c# 4.0 异步
异步编程已经存在了很长一段时间。 .NET中引入了async和await关键字,使我们能够编写可以轻松利用异步优势的程序。 但是,直到C#8.0中IAsyncEnumerable <T>到来之前,还没有任何方法可以异步使用数据流。
IAsyncEnumerable <T>与用于遍历集合的IEnumerable <T>方法相似,不同之处在于IAsyncEnumerable <T>允许我们异步遍历集合。 换句话说,IAsyncEnumerable <T>允许我们在不阻塞线程的情况下等待集合中的下一个元素。
[ 同样在InfoWorld上:TypeScript与JavaScript:了解差异 ]
在本文中,我们将通过相关的代码示例来看看IAsyncEnumerable <T>解决的挑战。 若要使用本文提供的代码示例,您应该在系统中安装Visual Studio 2019。 如果您还没有副本,则可以在此处下载Visual Studio 2019 。
在Visual Studio 2019中创建控制台应用程序项目
首先,让我们在Visual Studio中创建一个.NET Core控制台应用程序项目。 假设系统中已安装Visual Studio 2019,请按照以下概述的步骤在Visual Studio 2019中创建新的.NET Core控制台应用程序项目。
- 启动Visual Studio IDE。
- 点击“创建新项目”。
- 在“创建新项目”窗口中,从显示的模板列表中选择“控制台应用程序(.NET Core)”。
- 点击下一步。
- 在接下来显示的“配置新项目”窗口中,指定新项目的名称和位置。
- 单击创建。
这将在Visual Studio 2019中创建一个新的.NET Core控制台应用程序项目。在本文的后续部分中,我们将使用该项目来处理IAsyncEnumerable <T>和异步流。
在Visual Studio 2019中指定语言版本
为了能够在Visual Studio中使用C#8.0,必须像我们所做的那样使用针对.NET Core的项目。 您还需要更改项目中使用的语言的语言版本。 为此,请按照以下概述的步骤操作:
- 右键单击该项目。
- 选择“属性”以调用属性窗口。
- 单击构建->高级。
- 单击语言版本的下拉控件。
- 选择C#8.0作为语言版本。
- 单击确定。
在C#8.0中的IAsyncDisposable,IAsyncEnumerable <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; }
}
[ 也在InfoWorld上:2020年探索的5种Microsoft开发人员工具和技术 ]
为什么要使用异步流?
想象一下,您有一个数据访问库,该库可以从数据存储中读取数据,并一次性发送所有结果。 您可以轻松实现这种方法。 您需要做的只是进行一些异步调用,以从基础数据存储中获取数据,然后立即返回所有数据。
只要您不需要在页面中返回数据,此解决方案就可以了。 在这种情况下,您可能必须进行多次调用才能返回数据。 在这方面,最好的解决方案是您可以在数据可用后立即将其发送回调用方。
这正是异步流可以拯救的地方。 如果返回数据的方法是同步的,则可以将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方法是异步的,但是此代码将一次返回所有这些数字,而不是一一生成就一一返回。
[ 同样在InfoWorld上:如何在ASP.NET Web API中记录请求和响应元数据 ]
这就是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);
}
}
}
}
[ 通过InfoWorld Daily新闻通讯了解软件开发,云计算,数据分析和机器学习方面的最新发展 ]
对IAsyncEnumerable <T>(又名异步流)的支持是C#8.0中最重要的新功能之一。 您可以在应用程序中利用异步流来使代码更整洁,更高效和高性能。 若要使用C#8.0编译器,您将需要Visual Studio 2019版本16.3或.NET Core 3.0 SDK。
翻译自: https://www.infoworld.com/article/3531251/how-to-use-asynchronous-streams-in-csharp-80.html
c# 4.0 异步