如何在 C# 8 中使用 異步流
本文轉(zhuǎn)載自微信公眾號「碼農(nóng)讀書」,作者碼農(nóng)讀書。轉(zhuǎn)載本文請聯(lián)系碼農(nóng)讀書公眾號。
異步編程已經(jīng)流行很多年了,.NET 引入的 async 和 await 關(guān)鍵詞讓異步編程更具有可讀性,但有一個遺憾,在 C# 8 之前都不能使用異步的方式處理數(shù)據(jù)流,直到 C# 8 引入的 IAsyncEnumerable
說到 IAsyncEnumerable
IAsyncDisposable, IAsyncEnumerable<T>, IAsyncEnumerator<T>
異步流 允許我們可以用異步的方式處理數(shù)據(jù),在這之前要了解下面三個接口:IAsyncDisposable, IAsyncEnumerable<T> 和 IAsyncEnumerator<T>,他們都是在 .NET Standard 2.1 中被引入,下面的代碼片段展示了這三個接口的定義。
- 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; }
- }
為什么要使用異步流
可以想象一下你有一個數(shù)據(jù)訪問層需要從數(shù)據(jù)庫中一次性讀取所有的數(shù)據(jù),要想使用這個功能很簡單,可以直接調(diào)用 底層提供的異步方法 XXXAsyc 實現(xiàn)異步調(diào)用并且一次性返回所有數(shù)據(jù)。
只要不是將所有數(shù)據(jù)都呈現(xiàn)在頁面上的話,這種解決方案問題不是太大,很多時候更多的是通過 分頁讀取 的形式,其實在這方面還有一個比較好的做法就是在數(shù)據(jù)可用時立即返回給調(diào)用者。
準(zhǔn)確的說,這里可使用 異步流 的方式來解決,如果你的方法是同步返回的話,你可以使用 return yield + 返回值 IEnumerable
最好的解決方案就是 return yield + 返回值 IAsyncEnumerable
在 C#8 中創(chuàng)建異步流
下面的代碼片段展示了一個返回 Task
- class Program
- {
- const int DELAY = 1000;
- const int MIN = 1;
- const int MAX = 10;
- public static async Task Main(string[] args)
- {
- foreach (int number in await GetData())
- {
- Console.WriteLine($"{DateTime.Now}: number={number}");
- }
- Console.ReadLine();
- }
- public 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;
- }
- }
當(dāng)運行上面的應(yīng)用程序,它會等待 10s 之后再將所有的 1-10 的數(shù)字輸出控制臺上,雖然這個 GetData 是異步的,但最終還是一次性輸出了,而不是一個一個的隔秒輸出。
這個時候可以讓 yield 關(guān)鍵詞介入,它是在 C# 2.0 中被引入的,常用于執(zhí)行狀態(tài)迭代 并且按一個一個的從集合中返回數(shù)據(jù),你不需要像上面一樣創(chuàng)建一個集合(integers) 再返回上去,下面的代碼片段是修改 GetData 方法并且合并了 yield 關(guān)鍵詞的版本,代碼如下:
- static async IAsyncEnumerable<int> GetData()
- {
- for (int i = MIN; i < MAX; i++)
- {
- yield return i;
- await Task.Delay(DELAY);
- }
- }
C#8 中使用異步流
要想使用異步流, 需要在 foreach 前增加一個 await 關(guān)鍵詞,如下代碼所示:
- public static async Task Main(string[] args)
- {
- await foreach (int number in GetData())
- {
- Console.WriteLine($"{DateTime.Now}: number={number}");
- }
- Console.ReadLine();
- }
下面是完整的僅供參考的代碼。
- class Program
- {
- const int DELAY = 1000;
- const int MIN = 1;
- const int MAX = 10;
- public static async Task Main(string[] args)
- {
- await foreach (int number in GetData())
- {
- Console.WriteLine($"{DateTime.Now}: number={number}");
- }
- Console.ReadLine();
- }
- static async IAsyncEnumerable<int> GetData()
- {
- for (int i = MIN; i < MAX; i++)
- {
- yield return i;
- await Task.Delay(DELAY);
- }
- }
- }
C# 8 中一個非常重要的特性就是支持了 IAsyncEnumerable
譯文鏈接:https://www.infoworld.com/article/3531251/how-to-use-asynchronous-streams-in-csharp-80.html