解析使用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;
}
猜你喜欢
- 顺序语句顺序顾名思义就是程序自上而下执行public class User { public static voi
- 一般文本文件我们以日志文件.log文件为例:import java.io.BufferedReader; import java.io.Fi
- 代码:package com.lwj.test.proxy;import java.lang.reflect.InvocationHandl
- class文件中的attributes_count和attributesattributes_count位于class文件中methods的
- 注:若是为了解决问题,可直接查看第二部分。1.安装与启动在下载安装前,请安装好JDK并配置好环境变量。ActiveMQ可到官网下载。点击进入
- 前言在我们公司里,不同的服务之间通过Feign进行远程调用,但是,我们在尝试使调用可重试时遇到了一个小问题,Feign框架本身可以配置的自己
- Java读取json数据并存入数据库1. pom依赖<dependency> &nbs
- 起因曾经用过西门子出的 * , 好处是直接有SDK开发包, 不会硬件开发也能直接使用缺点也是明显的, 就是只支持Windows系统, 另外就
- 前言我相信很多Android开发同学都遇到过这样的需求: 1.实现一个Splash界面,界面上有应用相关的背景图片和一个开始按钮.
- 一、思路将分页所需的内容都放到一个实体类中分页数据所需要的实体类!内包含页码,页大小,总条数,总页数,起始行pagehelpr提供了这个类
- 今天来记录一下,在项目中因为基本类型,所产生的bug包装类:8种基本类型的包装类应用场景:数据库建立实体映射多用包装类这两句话是重点:就是建
- final——final用于类表示该类不能派生子类。final用于方法表示该方法不能被子类重写。final用于变量时表示常量,类似于C/C+
- 前文说到 优雅的使用枚举参数 和 实现原理,本文继续说一下如何在 RequestBody 中优雅使用枚举。本文先上实战,说一下如何实现。在
- 下面笔者说说自己对进制转换的分析:笔者认为,任何进制都可以直接转换到十进制,而十进制也可以相当容易的转换到其他进制,所以笔者在这里将十进制作
- 一、项目简述功能: 主页显示商品; 所有蛋糕商品展示,可进行商品搜索; 点击商品进入商品详情页,具有立即购买功能,可增减购买商品数量亦可手动
- 所谓c#的委托就是说把函数当参数来传递。这个在js完全就用不着搞什么委托东西,直接转就是了。而对于C#来说则不是这样!一个函数,如果它的参数
- 分布式限流-单位时间多实例多线程访问次数限制接前面聊一聊redisson及优雅实现 和 说一说spring boot优雅集成redisson
- 在.NET中有三种计时器:一、 System.Windows.Forms命名空间下的Timer控件,和所在的Form属于同一个线程。Time
- FeignClient脱离eureka自定义URL需求Spring Cloud环境中的FeignClient有时候需要调用特定主机的接口,但
- //写注册表RegistryKey regWrite;//往HKEY_CURRENT_USER主键里的Software子键下写一个名为“Te