何为异步流

简介: 何为异步流

我们使用C#中的yield关键字可以实现迭代器,使用async和await关键字可以实现异步方法。异步流是这两种功能的结合体,它用异步方式生成和消费数据的迭代器。异步流是在C#8中引入的,它以IAsyncEnumerable<out T>IAsyncEnumerator<out T>: IAsyncDisposable两个接口为基础,这两个接口的代码如下:

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

在上面的代码中,从序列中获取每个元素的方法MoveNextAsync是一个异步操作,元素以零散的方式到达,就是异步流。IAsyncEnumerator接口中的的ValueTask是Task类型的轻量化封装,它的类型是结构类型,使用方式和Task差不多,但是它在同步完成任务时或者在返回马上可以使用的结果时可以减少内存开销,因此它比Task更加高效。


下面我们来看一看异步流的用法,首先我们定义一个计算斐波那契数列的方法Fibonacci。其中我们使用Thread.Sleep模拟一个耗时操作。代码如下:

IEnumerable<int> Fibonacci(int count)
{
    int prev = 1;
    int curr = 1;
    for (int i = 0; i < count; i++)
    {
        yield return prev;
        Thread.Sleep(1000000);
        int temp = prev + curr;
        prev = curr;
        curr = temp;
    }
}

在代码中Thread.Sleep(1000000)是同步的,也就是说只有Thread.Sleep(1000000)执行完成,后续代码才能继续执行。因此为了提高执行效率我们需要把Thread.Sleep(1000000)改成异步的,在这里我们就可以让它生成异步流。要生成异步流就需要同用到迭代器和异步方法。要实现这个功能就必须使用IAsyncEnumerable接口作为方法的返回类型。修改后的代码如下:

async IAsyncEnumerable<int> FibonacciAsync(int count)
{
    int prev = 1;
    int curr = 1;
    Random r = new();
    for (int i = 0; i < count; i++)
    {
        yield return prev;
        await Task.Delay(1000000);
        int temp = prev + curr;
        prev = curr;
        curr = temp;
    }
}

经过修改后,FibonacciAsync方法允许调用方以异步方式消费生成的数列,也就是说可以使用await foreach来遍历消费这个方法的返回结果。调用如下:

await foreach (var f in FibonacciAsync(200))
    Console.Write("{0} ", f);

修改后的代码虽然可以以一步的方式消费生成的数列,但是如果在LINQ查询语句中消费异步流是无法使用的。这时就需要引入System.Linq.Async,调用如下:

IAsyncEnumerable<int> query =
    from f in FibonacciAsync(200)
    where f % 2 == 1
    select f * 2;
await foreach (var number in query)
    Console.WriteLine(number);

如果想在ASP.NET Core的Action中返回异步流可以像下面这样做:

[HttpGet]
public async IAsyncEnumerable<string> Get()
{
    using var dbContext = new BookContext();
    await foreach (var userName in dbContext.Users
        .Select(u => u.Name)
        .AsAsyncEnumerable())
    yield return userName;
}

总结

异步流解决的是零散数据异步生成和消费问题。在 C#8以前一组数据只能以整体异步的方式返回给调用者。

目录
相关文章
|
7月前
|
存储 分布式计算 网络协议
流知识超详细总结!一文搞懂!
流知识超详细总结!一文搞懂!
55 5
流知识超详细总结!一文搞懂!
|
7月前
|
Java
Java线程通信的精髓:解析通知等待机制的工作原理
Java线程通信的精髓:解析通知等待机制的工作原理
80 3
Java线程通信的精髓:解析通知等待机制的工作原理
|
7月前
|
JavaScript 前端开发 Java
流的概念,怎么处理
流的概念,怎么处理
|
7月前
|
前端开发
请简述同步和异步的区别是什么
请简述同步和异步的区别是什么
63 2
|
Linux
44 # 流的原理
44 # 流的原理
62 0
|
7月前
|
安全 Go
无缓冲通道:Go语言同步之道
无缓冲通道:Go语言同步之道
69 0
|
消息中间件 Java 数据挖掘
异步响应的应用详谈
在传统的同步响应方式中,当一个请求发送到服务器时,服务器会立即进行处理,并在处理完成后返回结果给客户端。而在异步响应中,服务器在接收到请求后,不会立即进行处理,而是将请求放入一个队列中,然后继续处理其他请求。当请求完成处理后,服务器会通过回调函数或消息通知的方式将结果返回给客户端。
204 0
|
存储 SQL 设计模式
C#异步有多少种实现方式?
C#异步有多少种实现方式?
异步思维——把请求与解析分开
异步思维——把请求与解析分开
63 0
|
消息中间件 缓存 供应链
“消息驱动、事件驱动、流 ”基础概念解析
本文旨在帮助大家对近期消息领域的高频词“消息驱动(Message-Driven),事件驱动(Event-Driven)和流(Streaming)”有更清晰的了解和认知,其中事件驱动 EDA 作为 Gartner 预测的十大技术趋势之一, EventBridge 作为下一代消息中间件,也是目前的重点方向之一。
326 0
“消息驱动、事件驱动、流 ”基础概念解析