解析使用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;
}