使用enumerator模式简化异步操作
先看一段同步代码:
public int SumPageSizes(IList<Uri> uris) {
int total = 0;
foreach (var uri in uris) {
statusText.Text = string.Format("Found {0} bytes ...", total);
var data = new WebClient().DownloadData(uri);
total += data.Length;
}
statusText.Text = string.Format("Found {0} bytes total", total);
return total;
}
这段代码比较简单,使用同步方式一个一个的获取Uri的Data,然后进行统计。
如果要使用异步方式一个一个的统计,那应该如何计算呢?
我以前演示过一段丑陋的代码大致如下:
WebClient webClient = new WebClient();
webClient.DownloadDataCompleted += (s, e) =>
{
// 使用A对象,做些事情。
WebClient webClient2 = new WebClient();
webClient2.DownloadDataCompleted += (s2, e2) =>
{
//使用B对象,做些事情。
// 递归的去 DownloadDataAsync。
};
webClient2.DownloadDataAsync(new Uri("B 的地址"));
};
webClient.DownloadDataAsync(new Uri("A 的地址"));
当然如果你确定只有两个地址的话,这种方法未尝不可。如果有多个地址的话,则必须递归的调用了。
如何使用Enumerator来简化异步操作:
public void SumPageSizesAsync(IList<Uri> uris) {
SumPageSizesAsyncHelper(uris.GetEnumerator(), 0);
}
private void SumPageSizesAsyncHelper(IEnumerator<Uri> enumerator, int total) {
if (enumerator.MoveNext()) {
statusText.Text = string.Format("Found {0} bytes ...", total);
var client = new WebClient();
client.DownloadDataCompleted += (sender, e) => {
SumPageSizesAsyncHelper(enumerator, total + e.Result.Length);
};
client.DownloadDataAsync(enumerator.Current);
}
else {
statusText.Text = string.Format("Found {0} bytes total", total);
enumerator.Dispose();
}
}
通过SumPageSizesAsyncHelper ,可以实现异步调用A->异步调用B->异步调用C..的方式。
首先解释下为什么可以,假设uris 有A,B,C.
SumPageSizesAsyncHelper(uris.GetEnumerator(), 0);
方法先调用A,因为A后面还有B,所以enumerator.MoveNext()返回True,
接着在DownloadDataCompleted事件结束后,调用B,同样,因为B后面还有C,
所以enumerator.MoveNext() 继续返回True,接着在DownloadDataCompleted事件后调用C。
在调用C结束后,因为C后面没有了,所以enumerator.MoveNext() 返回False,
也可以认为全部都下载完毕了。所以返回最终的结果。
如果使用async 和await来实现的话,代码如下:
public async Task<int> SumPageSizesAsync2(IList<Uri> uris)
{
int total = 0;
Char charText = 'A';
foreach (var uri in uris)
{
var data = await new WebClient().DownloadDataTaskAsync(uri);
total += data.Length;
Console.WriteLine("Thread Id: {0}:调用{1}的地址 Found {2} bytes...{3}",
Thread.CurrentThread.ManagedThreadId, charText, total, DateTime.Now);
charText = Convert.ToChar(charText + 1);
}
Console.WriteLine("Thread Id: {0}:全部完成,Found {1} bytes total {2}",
Thread.CurrentThread.ManagedThreadId, total, DateTime.Now);
return total;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架