.NET 基于Task的异步编程模型

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

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关键字)


按照原来的写法:
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:
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这样的方法。

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 等)
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() 方法。

比如下面的方法:

static async void SimpleAsyncTest() { var client = new WebClient(); var result1 = await client.DownloadStringTaskAsync("http://www.csdn.net"); Console.WriteLine("complete"); }被转化为如下:注:Reflector反射出来的名字都是特殊字符,下面的类名变量名为了可读已做替换。
private static void SimpleAsyncTest() { SimpleAsyncTestObj obj = new SimpleAsyncTestObj(0); obj.MoveNextDelegate = new Action(obj, (IntPtr)this.MoveNext); obj.builder = AsyncVoidMethodBuilder.Create(); obj.MoveNext(); }

SimpleAsyncTestObj:
[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() 获取结果。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的设计,比如:
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。 以实现更加灵活的异步编程。
更多的 CTP Sample 可以看这里:http://www.wischik.com/lu/AsyncSilverlight/AsyncSamples.html


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值