解析使用enumerator模式简化异步操作的详解
发布时间:2021-10-08 01:44:54
先看一段同步代码:
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;
}


猜你喜欢
- Crypto 库是C/C++的加密算法库,这个加密库很流行,基本上涵盖了市面上的各类加密解密算法,以下代码是我在学习是总结的,放到这里用于后
- 前言.NET中的委托是一个类,它定义了方法的类型,是一个方法容器。委托把方法当作参数,可以避免在程序中大量使用条件判断语句的情况。项目名为T
- 什么是容器?在Java的GUI界面设计中,关于容器的理解,从字面意思我们就可以认为它是存放控件的地方,而这个地方依托在窗体之上,常用的容器是
- StringDictionary:默认key不区分大小写 NameValueCollection:默认key区分大小写 KeyedColle
- java 中Spark中将对象序列化存储到hdfs摘要: Spark应用中经常会遇到这样一个需求: 需要将JAVA对象序列化并存储到HDFS
- 下面程序代码通过使用Lock锁执行简单的流水线任务:import java.util.concurrent.locks.Condition;
- 说明Druid是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。DruidDataSource支持的数据库:理论上
- 在软件开发过程中经常需要知道程序运行的大概时间,或者需要在规定时间内取数据,这是可以使用下面的方法获取时间段,还可以用在限时循环方法一:/*
- 在开始本文前先考虑以下一个问题在不使用任何带有自动补全功能IDE的情况下,如何获取一个数组的长度?以及,如何获取一个字符串的长度?这个问题我
- namespace PadWebServices.Model{ public static class DataTa
- 附GitHub源码:WebViewExplore先看图:在WebView页面长按时会弹出一个复制框,但如果里面的item不是我们想要的或者想
- 用了多年的Visual Studio,今天才发现这个编码技巧,真是惭愧,分享出来,算是抛砖引玉吧!开发环境: vs2010+C#1、代码重构
- 一、方法(Method)概念 1、Java 中的方法就是其他编程语言中的函数(Function) 2、方法的定义格式:①
- 更新了AS 3.1.2之后,发现新建Kotlin类,类注释依然木有,没办法只有自己动手了。方法很简单,编辑File Header就可以啦。只
- volatile 变量提供了线程的可见性,并不能保证线程安全性和原子性。什么是线程的可见性:锁提供了两种主要特性:互斥(mutual exc
- 上一篇实现了移动端微信消息界面功能,以此为基础继续完善服务端功能服务端微信消息页实现微信消息界面的实现,和登录,注册是类似的,无非就是接受客
- 协程属于Kotlin中非常有特色的一项技术,因为大部分编程语言中是没有协程这个概念的。那么什么是协程呢?它其实和线程有点相似,可以简单地将它
- 一、IO流的分类字符流ReaderInputStreamReader(节点流)BufferedReader(处理流)WriterOutput
- 做快递面单打印模板,快递要求纸张大小100 x 150mm。PageSize.A4=595 x 842A4尺寸=210mm×297mm故设置
- java @Value("${}")获取不到配置文件中值1、property.yml配置spring: ma