文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

C#.NET 拾遗补漏之理解 C# 中的异步流

2024-12-02 13:14

关注

本文转载自微信公众号「精致码农」,作者liamwang  。转载本文请联系精致码农公众号。

在阅读本文前,建议先阅读本系列的上一篇文章『理解 yield 关键字』。我们知道,使用 C# 的 yield 关键字可以实现一个迭代器(Iterator),使用 async/await 关键字可以实现一个异步方法。异步流(Asynchronous Stream)就是这两种功能的结合体,它实现了以异步的方式生成和消费一组数据系列的迭代器。

异步流的支持主要建立在 C# 8 引入的两个接口上:

  1. public interface IAsyncEnumerable<out T> 
  2.     IAsyncEnumerator GetAsyncEnumerator (...); 
  3. public interface IAsyncEnumerator<out T>: IAsyncDisposable 
  4.     T Current { get; } 
  5.     ValueTask MoveNextAsync(); 

所以理解了上一篇我们讲的 yield 关键字,就很容易理解异步流,它只是在原来的基础上支持通过 yield return 返回异步得到的一系列结果值而已。从序列中获取每个元素的行为(MoveNextAsync)是一个异步操作,元素是以零散的方式到达,这就形成了所谓的“异步流”(比如视频流中的数据)。

这里 IAsyncEnumerator 接口的 ValueTask 是 Task 类型轻量化的封装,它是结构类型(值类型)。使用方式与 Task 相似,但它在同步完成任务或返回立即可用的结果时(这在列举序列时会经常发生),可以避免不必要的内存开销,比 Task 更高效。

在上一篇文章中的 Fibonacci 方法中,Thread.Sleep(1000) 用来模拟一个耗时操作,它是“同步”的:

  1. IEnumerable<int> Fibonacci(int count
  2.     int prev = 1; 
  3.     int curr = 1; 
  4.     for (int i = 0; i < count; i++) 
  5.     { 
  6.         yield return prev; 
  7.         Thread.Sleep(1000); 
  8.         int temp = prev + curr; 
  9.         prev = curr; 
  10.         curr = temp
  11.     } 

为了提高程序执行效率,我们很有可能需要把 Thread.Sleep(1000) 改成“异步”的。如果使它生成异步的数据流,该怎么做呢?这就需要同时用到迭代器和异步方法了,也就是说方法中要同时使用 yield return 和 async/await 关键字。要支持这一特性,就要使用 IAsyncEnumerable 作为方法的返回类型。于是,前文的 Fibonacci 方法可以这样改造:

  1. async IAsyncEnumerable<int> FibonacciAsync(int count
  2.     int prev = 1; 
  3.     int curr = 1; 
  4.     Random r = new(); 
  5.     for (int i = 0; i < count; i++) 
  6.     { 
  7.         yield return prev; 
  8.         await Task.Delay(1000); 
  9.         int temp = prev + curr; 
  10.         prev = curr; 
  11.         curr = temp
  12.     } 

不同的是,这个方法允许调用者以异步的方式消费它生成的数字系列,换句话说就是使用 await foreach 来遍历消费这个方法的返回结果(IAsyncEnumerable),如下所示:

  1. await foreach (var n in FibonacciAsync(10)) 
  2.     Console.Write("{0} ", n); 

但,如果要在 LINQ 查询语句中消费异步流,需要先引入 System.Linq.Async NuGet 包,除此之外,使用方式没有差别:

  1. IAsyncEnumerable<int> query = 
  2.     from i in FibonacciAsync(10) 
  3.     where i % 2 == 0 
  4.     select i * 10; 
  5.  
  6. await foreach (var number in query) 
  7.     Console.WriteLine(number); 

另外,在 ASP.NET Core 的 Action 方法中也支持返回异步流,如下面示例:

  1. [HttpGet] 
  2. public async IAsyncEnumerable Get() 
  3.     using var dbContext = new BookContext(); 
  4.     await foreach (var title in dbContext.Books 
  5.         .Select(b => b.Title) 
  6.         .AsAsyncEnumerable()) 
  7.     yield return title; 

综上,可以看到,异步流解决了零散数据系列的异步生成和消费问题。要知道,在 C# 还没有异步流时,一组数据系列(IEnumerable)只能以整体异步的方式(Task

参考:

  1. 《C# 9.0 in a nutshell》 
  2. https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/tutorials/generate-consume-asynchronous-stream 

 

来源:精致码农内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯