Visual Studio Async CTP的实现原理浅析 - 如何不使用async和await关键字来实现Async
2011-03-31 22:20
1051 查看
AsyncCTP为我们在单线程实现异步操作开辟了一条大道,尤其对于SL中的WCF来说让我们从繁琐的事件处理中解脱出来,本来我想写一个SL中使用Socket实现的RPC框架(正在我项目中使用)的系列笔记,不过有朋友提到了应该用AsyncCTP来规避WCF的异步方法带来的繁琐,事实上AsyncCTP在我项目中还不够灵活(应该是WCF对我的应用来说不够灵活),不过还是先放下那个系列,先看看AsyncCTP可以做些什么。
本文不会介绍AsyncCTP的使用方法,只分析在不改动SLCLR的基础上编译器如何根据async和await两个关键字对我们的代码做出正确的改动以达到单线程异步操作。
关于单线程异步可以参考ProgramminguserinterfacesusingF#workflows
要理解AsyncCTP强力推荐此文AsynchronousProgramminginC#usingIterators
先来看看AsyncCtpLibrary_Silverlight.dll中的几个重要的类,在System.Runtime.CompilerServices命名空间中找出这几个类AsyncMehodBuilder,AsyncMethodBuilder<TResult>,VoidAsyncMethodBuilder,TaskAWaiter,TaskAwaiter<TResult>,以及System.Threading.Tasks下面的Task,Task<TResult>看到名字基本可以知道是干啥的了,简单介绍一下:
AsyncMethodBuilder:假如async被施加在一个Task对象之前,编译器使用这个类。
AsyncMethodBuilder<TResult>:假如async被施加在Task<TResult>之前,编译器使用这个类。
VoidAsyncMethodBuilder:async被施加在一个方法声明之前,编译器使用这个类。(因此只有返回值是void的方法能加async关键字)
TaskAwaiter:对应在Task对象之前的await关键字。
TaskAwaiter<TResult>:对应在Task<TResult>对象之前的await关键字。
Task、Task<TResult>:对应两个await。
为了实现async,await编译器将每个被async关键字标记的方法编译为一个方法所在类的一个内嵌类,所有在方法体内出现的变量会被声明为这个类的field,如果是一个实例方法,那么this所代表的对象也被声明为一个field。这个类有两个核心成员:一个int来保存代码执行到那一步,暂且叫它step,一个方法来执行真正的动作,暂且叫做NextStep,整个逻辑看起来应该是这样的:
[code]{
[/code]
是不是觉得和yieldreturn很像?
而在async标记的方法大体是如此,假设方法被编译为一个命为AsyncMethodClass的类:
[code]a.xxx=xxx;
[/code]
那么编译根据什么来决定一个方法分成几个块呢?实际上,编译器根据一个async方法中出现的await关键字来进行分布,假如有一个await,那么应该有2个Step,假如有2个await,那么应该有3个Step。每一个Step,应该是以上一个Step中的await的EndWait开始(第一个Step除外),并以下一await的BeginWait结束(最后一个Step除外)。看到这里就理解了为什么async只能被标记在无返回值的函数上,因为NextStep函数必须要是一个无返回值的Action类型,传递给await的BeginWait方法,因此在上述代码中,在最后的第7行没有办法使用理论上的:
弄明白了编译器干的活之后,最后来看一个实例,来自于AsyncCTPSamples的片段:
[code]{
[/code]
经过上面的理论,不使用async和await关键字,改写如下:
[code]TaskAwaiter<string>wait2;
[/code]
可以发现,执行结果和使用async和await一样。
AsyncCTP的核心其实是Task的设计,CTP库为WebClient和WCF实现了Async到Task的工作,对于项目中出现自定义的一些方法则需要自己去定义Task,不过通过自己的方法定义Task,并且使用本文所描述的方法进行包装(尤其是在原先使用Emit或者使用dynamic关键字来做AOP的场合),那么可以抛开aysnc和await关键字,阻止编译器对代码的改动,这样的好处是断点调试可以正常进行。
本文不会介绍AsyncCTP的使用方法,只分析在不改动SLCLR的基础上编译器如何根据async和await两个关键字对我们的代码做出正确的改动以达到单线程异步操作。
关于单线程异步可以参考
要理解AsyncCTP强力推荐此文
先来看看AsyncCtpLibrary_Silverlight.dll中的几个重要的类,在System.Runtime.CompilerServices命名空间中找出这几个类AsyncMehodBuilder,AsyncMethodBuilder<TResult>,VoidAsyncMethodBuilder,TaskAWaiter,TaskAwaiter<TResult>,以及System.Threading.Tasks下面的Task,Task<TResult>看到名字基本可以知道是干啥的了,简单介绍一下:
AsyncMethodBuilder:假如async被施加在一个Task对象之前,编译器使用这个类。
AsyncMethodBuilder<TResult>:假如async被施加在Task<TResult>之前,编译器使用这个类。
VoidAsyncMethodBuilder:async被施加在一个方法声明之前,编译器使用这个类。(因此只有返回值是void的方法能加async关键字)
TaskAwaiter:对应在Task对象之前的await关键字。
TaskAwaiter<TResult>:对应在Task<TResult>对象之前的await关键字。
Task、Task<TResult>:对应两个await。
为了实现async,await编译器将每个被async关键字标记的方法编译为一个方法所在类的一个内嵌类,所有在方法体内出现的变量会被声明为这个类的field,如果是一个实例方法,那么this所代表的对象也被声明为一个field。这个类有两个核心成员:一个int来保存代码执行到那一步,暂且叫它step,一个方法来执行真正的动作,暂且叫做NextStep,整个逻辑看起来应该是这样的:
publicvoidNextStep()
[code]{
switch(step)
{
case1:
...
step++;
break;
case2:
...
step++;
break;
case3:
....
step++;
break;
.
.
.
}
}
[/code]
是不是觉得和yieldreturn很像?
而在async标记的方法大体是如此,假设方法被编译为一个命为AsyncMethodClass的类:
AsyncMethodClassa=newAsyncMethodClass();
[code]a.xxx=xxx;
a.yyy=yyy;
.
.
.
a.NextStep();
[/code]
那么编译根据什么来决定一个方法分成几个块呢?实际上,编译器根据一个async方法中出现的await关键字来进行分布,假如有一个await,那么应该有2个Step,假如有2个await,那么应该有3个Step。每一个Step,应该是以上一个Step中的await的EndWait开始(第一个Step除外),并以下一await的BeginWait结束(最后一个Step除外)。看到这里就理解了为什么async只能被标记在无返回值的函数上,因为NextStep函数必须要是一个无返回值的Action类型,传递给await的BeginWait方法,因此在上述代码中,在最后的第7行没有办法使用理论上的:
returna.NextStep();
弄明白了编译器干的活之后,最后来看一个实例,来自于AsyncCTPSamples的片段:
publicasyncvoidAsyncIntroSerial()
[code]{
varclient=newWebClient();
WriteLinePageTitle(awaitclient.DownloadStringTaskAsync(newUri("http://www.weather.gov")));
WriteLinePageTitle(awaitclient.DownloadStringTaskAsync(newUri("http://www.weather.gov/climate/")));
WriteLinePageTitle(awaitclient.DownloadStringTaskAsync(newUri("http://www.weather.gov/rss/")));
}
[/code]
经过上面的理论,不使用async和await关键字,改写如下:
TaskAwaiter<string>wait1;
[code]TaskAwaiter<string>wait2;
TaskAwaiter<string>wait3;
intstep=0;
WebClientwc=newWebClient();
privatevoidAsyncMethodWithoutKeywords()
{
switch(step)
{
case0:
Task<string>t1=wc.DownloadStringTaskAsync(newUri("http://www.weather.gov"));
step++;
wait1=t1.GetAwaiter<string>();
wait1.BeginAwait(newAction(AsyncMethodWithoutKeywords));
break;
case1:
WriteLinePageTitle(wait1.EndAwait());
Task<string>t2=wc.DownloadStringTaskAsync(newUri("http://www.weather.gov/climate/"));
step++;
wait2=t2.GetAwaiter<string>();
wait2.BeginAwait(newAction(AsyncMethodWithoutKeywords));
break;
case2:
WriteLinePageTitle(wait2.EndAwait());
Task<string>t3=wc.DownloadStringTaskAsync(newUri("http://www.weather.gov/rss/"));
step++;
wait3=t3.GetAwaiter<string>();
wait3.BeginAwait(newAction(AsyncMethodWithoutKeywords));
break;
case4:
WriteLinePageTitle(wait3.EndAwait());
break;
}
}
[/code]
可以发现,执行结果和使用async和await一样。
AsyncCTP的核心其实是Task的设计,CTP库为WebClient和WCF实现了Async到Task的工作,对于项目中出现自定义的一些方法则需要自己去定义Task,不过通过自己的方法定义Task,并且使用本文所描述的方法进行包装(尤其是在原先使用Emit或者使用dynamic关键字来做AOP的场合),那么可以抛开aysnc和await关键字,阻止编译器对代码的改动,这样的好处是断点调试可以正常进行。
相关文章推荐
- Visual Studio Async CTP的实现原理浅析 - 跳出Task,构建自己的Awaiter
- 如何在.net4.0中使用.net4.5的async/await实现异步
- 如何在.net4.0中使用.net4.5的async/await
- 为什么我们要使用Async、Await关键字
- 使用Typescript写的Vue初学者Hello World实例(实现按需加载、跨域调试、await/async)
- Visual Studio 2010的MSDN帮助文档,如何实现拷贝使用
- 如何:使用 C++ 实现 C# 的 is 和 as 关键字
- [C#] .NET4.0中使用4.5中的 async/await 功能实现异
- 【源代码】手把手教你Autolayout如何使用动画(附类似Passbook效果Demo+详细分析实现原理)
- 不使用回调函数的ajax请求实现(async和await简化回调函数嵌套)
- asp.net webform中使用async,await实现异步操作
- 不使用回调函数的ajax请求实现(async和await简化回调函数嵌套)
- .net 4.5如何使用Async和Await进行异步编程
- Java并发编程:Spring中使用 @async 注解实现异步调用的原理
- 可移植类库无法使用async、await关键字
- async和await关键字实现异步编程
- 【TypeScript】如何在TypeScript中使用async/await,让你的代码更像C#。
- [C#] .NET4.0中使用4.5中的 async/await 功能实现异步
- ORM,ASP.NET中ORM学习,ASP.NET中ORM学习心得,WEB2.0中ORM实现原理,Asp.net简单ORM示例源码详细讲解,Asp.net2.0:如何使用ObjectDataSource(配合ORM )
- 浅析php如何实现爬取数据原理