NET 4.5的async/await真是个神奇的东西,巧妙异常以致我不禁对其实现充满好奇,但一直难以窥探其门径。不意间读了此篇强文《Asynchronous Programming in C# using Iterators》,犹如醍醐灌顶,茅厕顿开,思路犹如尿崩。美玉不敢独享,故写此篇,将所学中一些思考与诸君共享,期抛砖引玉,擦出一些基情火花……

  强文《Asynchronous Programming in C# using Iterators》出自大牛,大牛眼界高远。故文中所述较为简略,而文中所附代码亦较为晦涩,鄙人驽钝,反复阅读思考数十遍,方品出些味道。故本篇会对原文代码一个最简化的提取,再进行分析。

  强文提到的用迭代器在C#中进行异步编程,最核心的思想就是通过yield return产生一个可IEnumerable<Asyncable>的集合,取出第一个Asyncable,执行Async方法并立即返回,将控制权交给上层调用方,同时Async方法在完成后会回调MoveNext继续遍历之前集合。(本篇提到的最底层的Async方法均是以Begin/End来实现,之前的随笔也说过async/await只是语法糖)

  大概画了个草图意思一下,花了很久时间也没能画得特别清晰明了,请见谅,有好的想法还望赐教。

  接下来我们根据具体的代码来分析,首先看一下Main方法。第一行是一个异步方法,我们期待的结果是第二行的输出在异步方法结束前执行。

        static void Main(string[] args)
{
AsyncMethod("http://www.microsoft.com").Execute(); Console.WriteLine("我先执行,不等你了"); Console.ReadLine();
}

  AsyncMethod方法返回了一个IEnumerable<IAsync>的集合,这里需要注意的是AsyncMethod方法的返回值其实是一个类似状态机的类对象,这个对象本身不会执行内部的代码语句,我们需要一个Execute方法来开始遍历运行这个集合里的代码语句。而AsyncMethod方法里的语句又根据yield return的个数来划分成块,每一次MoveNext方法其实是执行一块的代码。也就是说存在多少个yield return,就会有多少次回调。

  

        static IEnumerable<IAsync> AsyncMethod(string url)
{
WebRequest req = HttpWebRequest.Create(url);
Console.WriteLine("[{0}] starting", url); // asynchronously get the response from http server
Async<WebResponse> response = req.GetResponseAsync();
yield return response; Console.WriteLine("[{0}] got response", url);
Stream resp = response.Result.GetResponseStream(); foreach (var item in resp.ReadToEndAsync())
{
yield return item;
} Console.WriteLine("done");
}

  GetResponseAsync方法看上去很简单,就是封装了一下Beginxx/Endxxx。为什么说看上去简单,后面会提到。AsyncPrimitive就是我们会接触到最底层的Asyncable对象了,本篇一切异步都是基于它来实现的。

        public static Async<WebResponse> GetResponseAsync(this WebRequest req)
{
return new AsyncPrimitive<WebResponse>(req.BeginGetResponse, req.EndGetResponse);
}

  ReadToEndAsync和AsyncMethod方法一样,是建立在可返回Async<T>对象的已有Async方法的基础上。

        public static IEnumerable<IAsync> ReadToEndAsync(this Stream stream)
{
MemoryStream ms = new MemoryStream();
int read = -1;
while (read != 0)
{
byte[] buffer = new byte[512];
Async<int> count = stream.ReadAsync(buffer, 0, 512);
yield return count; Console.WriteLine("[{0}] got data: {1}", "url", count.Result);
ms.Write(buffer, 0, count.Result);
read = count.Result;
}
}

  ReadAsync同样是通过Beginxxx/Endxxx来实现异步。他和GetResponseAsync一样是建立在AsyncPrimitive对象上的Async方法。

        public static Async<int> ReadAsync(this Stream stream, byte[] buffer, int offset, int count)
{
return new AsyncPrimitive<int>(
(callback, st) => stream.BeginRead(buffer, offset, count, callback, st),
stream.EndRead);
}

  下面让我们重点来看一下AsyncPrimitive类,你可能已经发现这个类和Task<T>.Factory.FromAsync方法有点相似。可是如果让自己来实现,怕不是想象的那么简单。

    public class AsyncPrimitive<T> : Async<T>
{
Action<Action<T>> func; public AsyncPrimitive(Func<AsyncCallback, object, IAsyncResult> begin, Func<IAsyncResult, T> end)
{
this.func = (cont) => begin(delegate(IAsyncResult res) { cont(end(res)); }, null);
} public override void ExecuteStep(Action cont)
{
func((res) =>
{
result = res;
completed = true;
cont();
});
}
}

  完全由委托、匿名方法和lambda表达式组成的类。在ExecuteStep被调用前,它不会做任何事情,仅仅是构建了一个Action<Action<T>>的委托。那么分析这个类才是本篇最主要的目的,但难点在于这货不是三言两语就能说清楚的,鄙人在晕乎了很久很久以后,将该类翻译如下,去除了所有的匿名方法和lambda表达式。看明白了这个类,就明白了通过迭代器是如何实现异步的。

    public class AsyncPrimitive<T> : Async<T>
{
Action<Action<T>> func; public AsyncPrimitive(Func<AsyncCallback, object, IAsyncResult> begin, Func<IAsyncResult, T> end)
{
this.Begin = begin;
this.End = end; this.func = this.ActionActionT;
} Func<IAsyncResult, T> End { get; set; }
Func<AsyncCallback, object, IAsyncResult> Begin { get; set; }
Action<T> RunInCallback { get; set; }
Action ActionOuter {get;set;} private void Callback(IAsyncResult ar)
{
this.RunInCallback(this.End(ar));
} private void ActionActionT(Action<T> cont)
{
this.RunInCallback = cont;
this.Begin(this.Callback, null);
} private void ActionT(T res)
{
this.result = res;
this.completed = true;
this.ActionOuter();
} public override void ExecuteStep(Action cont)
{
this.ActionOuter = cont;
this.func(this.ActionT);
}
}

  直观的就可以感觉到lambda帮助我们省略了多少代码,在简洁的同时,也增加了些许理解的难度。代码就是最好的注释,我实在没信心去用文字描述这个类如何工作。最后附上可运行的工程供调试用。原文链接开头已给出,原文中也给出了原文代码的下载。推荐都下载比对着看,可能会更有帮助。

  本篇也是初写的时候信心满满,不知道被各位吐槽后会是怎样一副情景……之后还应该还会有第二篇,也许是明天,也许是明年……

代码下载

 
 
 
标签: c#, await, async, 异步

Gulp:新一代前端构建利器

1、什么是Gulp

gulp.js 是一种基于流的,代码优于配置的新一代构建工具。

Gulp 和 Grunt 类似。但相比于 Grunt 的频繁的 IO 操作,Gulp 的流操作,能更快地完成构建。

2、Gulp特性

  • 使用方便

通过代码优于配置的策略,Gulp可以让简单的任务简单,复杂的任务更可管理。

  • 构建快速

通过流式操作,减少频繁的 IO 操作,更快地构建项目。

  • 插件高质

Gulp 有严格的插件指导策略,确保插件能简单高质的工作。

  • 易于学习

少量的API,掌握Gulp可以毫不费力。构建就像流管道一样,轻松加愉快。

3、Gulp安装

Gulp是基于 Node.js的,故要首先安装 Node.js

1 npm install -g gulp
2 npm install —-save-dev gulp

4、Gulp使用

Gulp的任务都是以插件的形式存在,本次示例以 gulp-jshint 为例,展示Gulp的常规用法。

安装 gulp-jshint

1 npm install gulp-jshint --save-dev

创建 gulpfile.js

gulp项目页 有一个 Sample gulpfile。如果不会写的话,直接参考一下就OK了。

 1 var gulp = require('gulp');
2 var jshint = require('gulp-jshint');
3
4 var paths = {
5 scripts: 'js/**/*.js',
6 };
7
8 gulp.task('lint', function() {
9 return gulp.src(paths.scripts)
10 .pipe(jshint())
11 .pipe(jshint.reporter('default'));
12 });

然后执行命令行

1 gulp lint

即可。

5、Gulp总结

Gulp 相比于 Grunt 有很多优点,比较直观的:就是学习曲线比较平滑。比Grunt速度更快、配置更少。

当然,Gulp还有很多高级的特性,详见官方文档

Gulp插件列表

http://gulpjs.com/

仿async/await(一)and Gulp:新一代前端构建利器的更多相关文章

  1. Gulp:新一代前端构建利器

    1.什么是Gulp gulp.js 是一种基于流的,代码优于配置的新一代构建工具. Gulp 和 Grunt 类似.但相比于 Grunt 的频繁的 IO 操作,Gulp 的流操作,能更快地完成构建. ...

  2. 使用Gulp实现前端构建自动化

    使用Gulp实现前端构建自动化 安装 一.安装NodeJs Gulp的安装依赖于NodeJs的npm安装管理器 安装包下载地址 关于npm命令: 1. npm install <name> ...

  3. 前端构建利器Grunt—Bower

    runt + Bower—前端构建利器 目前比较流行的WEB开发的趋势是前后端分离.前端采用重量级的Javascript框架,比如Angular,Ember等,后端采用restful API的Web ...

  4. 学习迭代器实现C#异步编程——仿async/await(一)

    .NET 4.5的async/await真是个神奇的东西,巧妙异常以致我不禁对其实现充满好奇,但一直难以窥探其门径.不意间读了此篇强文<Asynchronous Programming in C ...

  5. Grunt + Bower—前端构建利器(转)

    目前比较流行的WEB开发的趋势是前后端分离.前端采用重量级的Javascript框架,比如Angular,Ember等,后端采用restful API的Web Service服务,通过JSON格式进行 ...

  6. Grunt + Bower—前端构建利器

    目前比较流行的WEB开发的趋势是前后端分离.前端采用重量级的Javascript框架,比如Angular,Ember等,后端采用restful API的Web Service服务,通过JSON格式进行 ...

  7. 前端构建大法 Gulp 系列 (四):gulp实战

    前端构建大法 Gulp 系列 (一):为什么需要前端构建 前端构建大法 Gulp 系列 (二):为什么选择gulp 前端构建大法 Gulp 系列 (三):gulp的4个API 让你成为gulp专家 前 ...

  8. 前端构建大法 Gulp 系列 (三):gulp的4个API 让你成为gulp专家

    系列目录 前端构建大法 Gulp 系列 (一):为什么需要前端构建 前端构建大法 Gulp 系列 (二):为什么选择gulp 前端构建大法 Gulp 系列 (三):gulp的4个API 让你成为gul ...

  9. 前端构建大法 Gulp 系列 (二):为什么选择gulp

    系列目录 前端构建大法 Gulp 系列 (一):为什么需要前端构建 前端构建大法 Gulp 系列 (二):为什么选择gulp 前端构建大法 Gulp 系列 (三):gulp的4个API 让你成为gul ...

随机推荐

  1. 详细解释VB连接access几种方法数据库

    在VB中,连接ACCESS数据库的方法主要有以下三种 使用ADO对象,通过编写代码訪问数据库 Connection 对象 ODBC数据源 使用ADO Data 控件高速创建数据库连接 有三种连接方法 ...

  2. tmp1

    program1: Line # Mem usage Increment Line Contents================================================ 2 ...

  3. shell文字过滤程序(十一):paste命令

    [版权声明:转载请保留源:blog.csdn.net/gentleliu.Mail:shallnew at 163 dot com] 由于可以从字面上可以看出.paste指挥和cut相反的命令.cut ...

  4. NSOJ 一个人的旅行(图论)

    虽然草儿是个路痴(就是在杭电待了一年多,居然还会在校园里迷路的人,汗~),但是草儿仍然很喜欢旅行,因为在旅途中 会遇见很多人(白马王子,^0^),很多事,还能丰富自己的阅历,还可以看美丽的风景……草儿 ...

  5. MySQL中游标使用以及读取文本数据

    原文:MySQL中游标使用以及读取文本数据 前言 之前一直没有接触数据库的学习,只是本科时候修了一本数据库基本知识的课.当时只对C++感兴趣,天真的认为其它的课都没有用,数据库也是半懂不懂,胡乱就考试 ...

  6. 收藏的一些javascript片段

    原文:收藏的一些javascript片段 学习js也很有一段时间,收集了一些js的片段.特地整理排版了一下,以一个js初学者的视界来分析注释了这些代码段,暂且不去讨论它的性能和优化问题,相信会对一些初 ...

  7. React.js终探(六)

    在React中,我们怎么样使用它跟CSS 3 动画结合在一起使用呢? CSS3 Transitoin CSS3动画要求DOM属性变化的时候才能够被触发.所以这就需要我们把属性变化后的React元素渲染 ...

  8. java_软件发布版本_Asynch HttpClien 对比发行版本说明_Alpha、Beta、RC、GA版本的区别

    今天看了一天的apache 的httpasyncclient ,为了和默认的httpclient做个对比,httpcore是核心轻量级的提供传统阻塞IO 和 java NIO两种方式,httpclie ...

  9. 打破了中国电信华为无线路由猫(HG522-C)自己主动拨号+任意数量的计算机+iTV

    中国电信路由猫去势后总是我的好E家里到处都是卖包(够坏垄断市场.有霸王条款多,例如,他们必须用自己的手机,同时计算机的最大数量的在线等),我曾破获另一家中国电信路由猫.非常easy,由U它磁盘恢复默认 ...

  10. linux_后台启动多个java -jar 程序,及关闭

    启动脚本 startup.sh #!/bin/bash x=$(($)) )) do java -jar /home/chenpenghui/crawler/crawler-hb/StartUpIp. ...