前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >何为异步流

何为异步流

作者头像
喵叔
发布2021-12-15 09:01:31
2810
发布2021-12-15 09:01:31
举报
文章被收录于专栏:喵叔's 专栏喵叔's 专栏

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

代码语言:javascript
复制
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模拟一个耗时操作。代码如下:

代码语言:javascript
复制
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接口作为方法的返回类型。修改后的代码如下:

代码语言:javascript
复制
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来遍历消费这个方法的返回结果。调用如下:

代码语言:javascript
复制
await foreach (var f in FibonacciAsync(200))
    Console.Write("{0} ", f);

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

代码语言:javascript
复制
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中返回异步流可以像下面这样做:

代码语言:javascript
复制
[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以前一组数据只能以整体异步的方式返回给调用者。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-12-14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档