求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
     
   
分享到
.NET 基于Task的异步编程模型
 

发布于2011-09-14

 

最近下载了Visual Studio Async CTP,体验了下基于Task的异步编程带来的新特性。在CTP中,增加了新的关键字: async, await。尤其是在SL,WP7的编程中,大量使用异步调用的环境里,async, await的确能减少编程的复杂度。看上去像是同步的方法,其实编译器做了些手脚,悄悄的生成了回调的代码。比如:

view plain

private async void button_Click(object sender, EventArgs e)

{

var client = new WebClient();

var result = await client.DownloadStringTaskAsync("http://www.csdn.net");

textBox1.Text = result;

MessageBox.Show("Complete");

}

上面的代码,await 这一行的 DownloadStringTaskAsync (CTP中AsyncCtpLibrary.dll中提供的WebClient的扩展方法) 是异步执行的,之后两行被包装成回调。

(另外,注意:在button_Click的void之前加上了async关键字)

按照原来的写法:

view plain

private void button3_Click(object sender, EventArgs e)

{

var client = new WebClient();

client.DownloadStringCompleted += (s, evt) => {

textBox1.Text = evt.Result;

MessageBox.Show("Complete");

};

client.DownloadStringAsync(new Uri("http://www.csdn.net"));

}

比较代码可以感觉到 async,await 的出现,一下把我们从“先定义回调”的思想变回“同步调用"的顺序时代。

先回顾一下目前为止我们使用的异步编程方法:

1. 最简单的Thread:

view plain

var thread = new Thread((obj) =>

{

// 模拟复杂的处理

Thread.Sleep(1000);

Console.WriteLine(obj);

});

thread.Start("some work");

这种处理方式,麻烦在于处理返回值上,通常还要设计个包装类封装个Result 属性以获得返回值。

2. Thread的包装演变出 APM (Asynchronous Programming Model):

最典型的代表:Delegate.BeginInvoke / EndInvoke。WCF的客户端代理类如果选择生成异步方法,那么也是BeginXXX,EndXXX这样的方法。

view plain

Func<string, string> func = x =>

{

// 模拟复杂的处理

Thread.Sleep(1000);

return x + " is completed";

};

func.BeginInvoke("some work", ir =>

{

AsyncResult ar = (AsyncResult)ir;

var delegateInstance = (Func<string, string>)ar.AsyncDelegate;

var result = delegateInstance.EndInvoke(ir);

Console.WriteLine(result);

}, null);

3. 基于事件的APM——EAP(Event-based Asynchronous Pattern)

上面的BeginXXX,EndXXX的异步编程模型,在Callback上还是略显笨重,因此又演变出基于事件注册回调方法的模式。

最典型的代表就是 WebClient (HttpWebRequest 等)

view plain

var client = new WebClient();

client.DownloadStringCompleted += (s, evt) => {

textBox1.Text = evt.Result;

MessageBox.Show("Complete");

};

client.DownloadStringAsync(new Uri("http://www.csdn.net"));

4. 基于Task的APM——TAP(Task-based Asynchronous Pattern)

.net 4.0 里引入了并行编程库,Task成为新的异步编程主角,async, await 语法糖应运而生。为了实现async,await编译器将每个被async关键字标记的方法编译为一个方法所在类的一个内嵌类,所有在方法体内出现的变量会被转为这个类的字段,如果是一个实例方法,那么this所代表的对象也被声明为一个字段。这个类有两个核心成员:一个int来保存代码执行到哪一步的state,一个方法来执行真正的动作的 MoveNext() 方法。

比如下面的方法:

view plain

static async void SimpleAsyncTest()

{

var client = new WebClient();

var result1 = await client.DownloadStringTaskAsync("http://www.csdn.net");

Console.WriteLine("complete");

}

被转化为如下:注:Reflector反射出来的名字都是特殊字符,下面的类名变量名为了可读已做替换。

view plain

private static void SimpleAsyncTest()

{

SimpleAsyncTestObj obj = new SimpleAsyncTestObj(0);

obj.MoveNextDelegate = new Action(obj, (IntPtr)this.MoveNext);

obj.builder = AsyncVoidMethodBuilder.Create();

obj.MoveNext();

}

SimpleAsyncTestObj:

view plain

[CompilerGenerated]

private sealed class SimpleAsyncTestObj

{

// Fields

private bool disposing;

public AsyncVoidMethodBuilder builder;

private int state;

public Action MoveNextDelegate;

private TaskAwaiter<string> awaiter;

public WebClient client;

public string result;

// Methods

[DebuggerHidden]

public SimpleAsyncTestObj(int state);

[DebuggerHidden]

public void Dispose();

public void MoveNext();

}

可以看到async方法被转化成类,然后调用了 MoveNext() ,初始的 state == 0,第一次调用如果没有 IsCompleted 则注册 awaiter 的 OnCompleted 事件,绑定的还是 MoveNext 方法。等awaiter 回调时,状态==1,直接进入 Label_0086 部分的代码,用 GetResult() 获取结果。

view plain

public void MoveNext()

{

try

{

string str;

bool flag = true;

if (this.state != 1)

{

if (this.state != -1)

{

this.client = new WebClient();

this.awaiter = this.client.DownloadStringTaskAsync("http://www.csdn.net").GetAwaiter<string>();

if (this.awaiter.IsCompleted)

{

goto Label_0086;

}

this.state = 1;

flag = false;

this.awaiter.OnCompleted(this.MoveNextDelegate);

}

return;

}

this.state = 0;

Label_0086:

str = this.awaiter.GetResult();

this.awaiter = new TaskAwaiter<string>();

this.result = str;

Console.WriteLine("over");

}

catch (Exception exception)

{

this.state = -1;

this.builder.SetException(exception);

return;

}

this.state = -1;

this.builder.SetResult();

}

如果方法中有多个 await 关键字的话,编译器生成的 MoveNext 则会是下面的样子:

public void MoveNext()

{

switch (state)

{

case 1:

...

state++;

// 注册第一个await对应异步的回调处理:回调中,获取结果,并调用下一个异步

break;

case 2:

...

state++;

// 注册第二个await对应异步的回调处理:回调中,获取结果,并调用下一个异步

break;

case 3:

....

state++;

break;

。。。

}

}

通过回调把下一个异步串起来,这么看来确实有 yield return 的感觉。看到这里相信大家都看出来在使用 TAP 编程时,最重要的是进行Task的设计,比如:

view plain

static async void DoSomeWork(int i, string arg)

{

await new TaskFactory().StartNew(() =>

{

var random = new Random();

// 模拟复杂的处理

Thread.Sleep(i * 1000);

Console.WriteLine(arg + " end");

});

}

还可以利用 TaskFactory.ContinueWhenAll / ContinueWhenAny 等特性编排 Task。 以实现更加灵活的异步编程。


相关文章

深度解析:清理烂代码
如何编写出拥抱变化的代码
重构-使代码更简洁优美
团队项目开发"编码规范"系列文章
相关文档

重构-改善既有代码的设计
软件重构v2
代码整洁之道
高质量编程规范
相关课程

基于HTML5客户端、Web端的应用开发
HTML 5+CSS 开发
嵌入式C高质量编程
C++高级编程

 
分享到
 
 
     


使用decj简化Web前端开发
Web开发框架形成之旅
更有效率的使用Visual Studio
MVP+WCF+三层结构搭建框架
ASP.NET运行机制浅析【图解】
编写更好的C#代码
10个Visual Studio开发调试技巧
更多...   


.NET框架与分布式应用架构设计
.NET & WPF & WCF应用开发
UML&.Net架构设计
COM组件开发
.Net应用开发
InstallShield


日照港 .NET Framework & WCF应用开发
神华信息 .NET单元测试
北京 .Net应用软件系统架构
台达电子 .NET程序设计与开发
赛门铁克 C#与.NET架构设计
广东核电 .Net应用系统架构
更多...