认识非同步程序开发设计模型

从VS2012开始引入的新的非同步程序设计的支持-------async/await设计模型

  1. 之前的当我们支持非同步作业的时候,往往使用多线程开解决,我们比较熟悉的就是
  2. 执行者:Thread,ThreadPool (线程和线程池,后者有利于资源的有效利用)
  3. 非同步的设计模型:Begin方法/End方法,Async事件/Completed事件(主要是异步委托之类的,我在我以前的博文中有写过专题)
  4. BackgroundWorker控制项
  5. Task Parallel Library

  虽然今天的重点是.NET4.5的async/await设计模式但是由于很多人对于.NET4.0中的Task仍然还是没有接触过,Task也是.NET 4.5 async await的基础概念之一,值得大家花点时间熟悉,那么这里就将他也作为一个附加专题来做一下讲解。

附属专题:.NET4.0多线程开发之利器---àTask

到我们在开发SignalR程序的时候,就必须要使用到多线程,假设没有.NET4.5的支持,那么你可能想到的最简单方式就是使用Task,它取代了传统的Thread,TheadPool的写法,能大幅度的简化同步逻辑的写法,颇为便利,下面我们来看几个典型的范例。

范例1:简单的开始

Test1()用以另一Thread执行Thread.Sleep()及Console.WriteLine(),效果与ThreadPool.QueueUserWorkItem()相当。

  private static void Test1()
{
//Task可以代替TheadPool.QueueUserWorkItem使用
Task.Factory.StartNew(() =>
{
Thread.Sleep();
Console.WriteLine("Done!");
});
Console.WriteLine("Async Run...");
}

StartNew()完会立刻执行下一行,故会先看到Aync Run,1秒后打印出Done。

范例2:等待各作业完成再继续下一步的应用场境

  同时启动多个作业多工并行(多线程并行),但要等待各作业完成再继续下一步的应用场境传统方式上可通过WaitHandle、AutoResetEvent、ManualResetEvent等机制实现;Task的写法相当简单,建立多個Task对象,再作为Task.WaitAny()或Task.WaitAll()的参数就搞定了!

private static void Test2()
{
var task1 = Task.Factory.StartNew(() =>
{
Thread.Sleep();
Console.WriteLine("Done!(3s)");
});
var task2 = Task.Factory.StartNew(() =>
{
Thread.Sleep();
Console.WriteLine("Done!(5s)");
});
//等待任意作业完成后继续
Task.WaitAny(task1, task1);
Console.WriteLine("WaitAny Passed");
//等待所有作业完成后继续
Task.WaitAll(task1, task2);
Console.WriteLine("WaitAll Passed");
}

task1耗时3秒、task2耗时5秒,所以3秒后WaitAny()执行完成、5秒后WaitAll()执行完毕。

范例3:如果要等待多工作业传回结果

通过StartNew<T>()指定传回类型建立作业,随后以Task.Result取值,不用额外Code就能确保多工作业执行完成后才读取结果继续运行

private static void Test3()
{
var task = Task.Factory.StartNew<string>(() =>
{
Thread.Sleep();
return "Done!";
});
//使用秒表计时
Stopwatch sw = new Stopwatch();
sw.Start();
//读取task.Result时,会等到作业完成传回值后才继续
Console.WriteLine("{0}", task.Result);
sw.Stop();
//取得task.Result耗时约2秒
Console.WriteLine("Duration: {0:N0}ms", sw.ElapsedMilliseconds);
}

实际执行,要花两秒才能跑完Console.WriteLine("{0}", task.Result),其长度就是Task執行并回传结果的时间。

范例4:在多工作业完成后接连运行行另一段程序可使用ContinueWith():

   private static void Test4()
{
Task.Factory.StartNew(() =>
{
Thread.Sleep();
Console.WriteLine("Done!");
}).ContinueWith(task =>
{
//ContinueWith会等待前面的任务完成才继续
Console.WriteLine("In ContinueWith");
});
Console.WriteLine("Async Run...");
}
如预期,ContinueWith()里的程序会在Task完成后才被执行。

范例5:多工作业时各段逻辑便会依顺序执行

.ContinueWith()传回值仍是Task对象,所以我们可以跟jQuery一样连连看,在ContinueWith()後方再接上另一个ContinueWith(),各段逻辑便会依顺序执行。

static void test5()
{
//ContinueWith()可以串接
Task.Factory.StartNew(() =>
{
Thread.Sleep();
Console.WriteLine("{0:mm:ss}-Done", DateTime.Now);
})
.ContinueWith(task =>
{
Console.WriteLine("{0:mm:ss}-ContinueWith 1", DateTime.Now);
Thread.Sleep();
})
.ContinueWith(task =>
{
Console.WriteLine("{0:mm:ss}-ContinueWith 2", DateTime.Now);
});
Console.WriteLine("{0:mm:ss}-Async Run...", DateTime.Now);
}

Task耗时两秒,第一個ContinueWith()耗時2秒,最后一个ContinueWith()继续在4秒后执行。

范例6:Task有监控状态的机制

ContinueWith()中的Action<Task>都会有一个输入参数,用于以得知前一Task的执行状态,有IsCompleted, IsCanceled, IsFaulted几个属性可用。要取消执行,得借助CancellationTokenSource及其所属CancellationToken类型,做法是在Task中持续呼叫CancellationToken.ThrowIfCancellationRequested(),一旦外部呼叫CancellationTokenSource.Cancel(),便会触发OperationCanceledException,Task有监控此异常状况的机制,将结束作业执行后续ContinueWith(),并指定Task.IsCanceled为True;而当Task程序发送Exception,也会结束触发ContinueWith (),此時Task.IsFaulted为True,ContinueWith()中可通过Task.Exception.InnerExceptions取得错误细节。以下程序同时可测试Task正常、取消及错误三种情景,使用者通过输入1,2或3来决定要测试哪一种。在Task外先声明一个CancellationTokenSource类型,将其中的Token属性当成StartNew()的第二项参数,而Task中則保留最初的五秒可以取消,方法是每隔一秒呼叫一次CancellationToken.ThrowIfCancellationRequested(),当程序外部调用CancellationTokenSource.Cancel(),Task就会結束。5秒后若未取消,再依使用者决定的测试情境return结果或是抛出Exception。ContinueWith()则会检查IsCanceled, IsFaulted等标识,并输出结果。

  private static void Test6()
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken cancelToken = cts.Token;//获取与此CancellationTokenSource关联的CancellationToken
Console.Write("Test Option 1, 2 or 3 (1-Complete / 2-Cancel / 3-Fault) : ");
var key = Console.ReadKey();
Console.WriteLine();
Task.Factory.StartNew<string>(() =>
{
//保留5秒检测是否要Cancel
for (var i = ; i < ; i++)
{
Thread.Sleep();
//如cancelToken.IsCancellationRequested
//引发OperationCanceledException
cancelToken.ThrowIfCancellationRequested();
}
switch (key.Key)
{
case ConsoleKey.D1: //选1时
return "OK";
case ConsoleKey.D3: //选3时
throw new ApplicationException("MyFaultException");
}
return "Unknown Input";
}, cancelToken).ContinueWith(task =>
{
Console.WriteLine("IsCompleted: {0} IsCanceled: {1} IsFaulted: {2}", task.IsCompleted, task.IsCanceled, task.IsFaulted);
if (task.IsCanceled)
{
Console.WriteLine("Canceled!");
}
else if (task.IsFaulted)
{
Console.WriteLine("Faulted!");
foreach (Exception e in task.Exception.InnerExceptions)
{
Console.WriteLine("Error: {0}", e.Message);
}
}
else if (task.IsCompleted)
{
Console.WriteLine("Completed! Result={0}", task.Result);
}
});
Console.WriteLine("Async Run...");
//如果要测Cancel,2秒后触发CancellationTokenSource.Cancel
if (key.Key == ConsoleKey.D2)
{
Thread.Sleep();
cts.Cancel();
}
}

Task能做的事,过去使用Thread/ThreadPool配合Event、WaitHandle一样能办到,但使用Task能以比较简洁的语法完成相同工作,使用.NET 4.0开发多线程时可多加利用。

到这里,我们继续回到原本的.NET4.5中,首先我们设计几种异步作业新旧写法法进行对比

利用WebClient类别下载网页内容

1.使用Async/Complete设计模式

 private static void DownLoadWebPageSourceCode_Old()
{
WebClient wc = new WebClient();
wc.DownloadStringCompleted += CompletedHandler;
wc.DownloadStringAsync(new Uri("http://www.cnblogs.com/rohelm"));
while (wc.IsBusy)
{
Console.WriteLine("还没下完,我喝一回茶!");
}
} private static void CompletedHandler(object sender, DownloadStringCompletedEventArgs e)
{
Console.WriteLine(e.Result);
}

运行效果如下:

2.使用新的async/await设计模式

 private static async void DownLoadWebPageSourceCode_New()
{
WebClient wc = new WebClient();
Console.WriteLine(await wc.DownloadStringTaskAsync("http://www.cnblogs.com/rohelm"));
}

而它的内部实现机制实际上是我们前面的附加专题中提到的Task,我们来查看下这个方法的源码:

[ComVisible(false), HostProtection(SecurityAction.LinkDemand, ExternalThreading=true)]
public Task<string> DownloadStringTaskAsync(string address)
{
return this.DownloadStringTaskAsync(this.GetUri(address));
} [ComVisible(false), HostProtection(SecurityAction.LinkDemand, ExternalThreading=true)]
public Task<string> DownloadStringTaskAsync(Uri address)
{
TaskCompletionSource<string> tcs = new TaskCompletionSource<string>(address);
DownloadStringCompletedEventHandler handler = null;
handler = delegate (object sender, DownloadStringCompletedEventArgs e) {
this.HandleCompletion<DownloadStringCompletedEventArgs, DownloadStringCompletedEventHandler, string>(tcs, e, args => args.Result, handler, delegate (WebClient webClient, DownloadStringCompletedEventHandler completion) {
webClient.DownloadStringCompleted -= completion;
});
};
this.DownloadStringCompleted += handler;
try
{
this.DownloadStringAsync(address, tcs);
}
catch
{
this.DownloadStringCompleted -= handler;
throw;
}
return tcs.Task;
}

3.取消非同步的方式

由于上面我们已经说过它的内部本质还是Task所以它的,取消该非同步作业依旧借助CancellationTokenSource及其所属CancellationToken类型

private static async Task TryTask()
{
CancellationTokenSource cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds());
Task<string> task = Task.Run(() => PirntWords("Hello,Wrold!", cts.Token), cts.Token);
Console.WriteLine(task.Result);
await task;
} private static string PirntWords(string input, CancellationToken token)
{
for (int i = ; i < ; i++)
{
Console.WriteLine(input);
token.ThrowIfCancellationRequested();
}
return input;
}

网页调用用多个Web服务/WCF服务/Http服务模型

       public async Task<ActionResult> DoAsync()
{
ServiceClient1 client1 = new ServiceClient1();
ServiceClient2 client2 = new ServiceClient2();
var task1 = client1.GetDataAsync();
var task2 = client2.GetDataAsync();
await Task.WhenAll(task1,task2);
return View("View名称",new DataModel(task1.Result,task2.Rusult));
}

是不是发现非常的方便,实用啊!

未完待续....

  后续内容:

   对WebAPI和WCF的进行一个简单比较,探讨WebAPI的机制,功能,架构,WinFrom Client/WebService Client大数据上传...备注:本文章版权的没有,归.NET使用者共有。

ASP.NET4.5Web API及非同步程序开发系列(1)的更多相关文章

  1. ASP.NET4.5Web API及非同步程序开发系列3

    ASP.NET4.5Web API及非同步程序开发系列(3) 接着上一篇博客的内容做一个补充,正好是一个大哥提出来的,我们看看一个有趣的现象. 请求相关问题的补充: 我们先在Controller中的定 ...

  2. ASP.NET4.5Web API及非同步程序开发系列

    ASP.NET4.5Web API及非同步程序开发系列 认识ASP.NET WEB API 他的前身为WCF WEB API用于协助WCF支持RestFul.现在集成进ASP.NET,正式更名为ASP ...

  3. ASP.NET4.5Web API及非同步程序开发系列(2)

    认识ASP.NET WEB API 他的前身为WCF WEB API用于协助WCF支持RestFul.现在集成进ASP.NET,正式更名为ASP.NET WEB API,ASP.NET Web API ...

  4. ASP.NET4.5Web API及非同步程序开发系列(3)

    接着上一篇博客的内容做一个补充,正好是一个大哥提出来的,我们看看一个有趣的现象. 请求相关问题的补充: 我们先在Controller中的定义一个我们在前一篇博客中已经测试过的方法如下: public ...

  5. 微信小程序开发系列六:微信框架API的调用

    微信小程序开发系列教程 微信小程序开发系列一:微信小程序的申请和开发环境的搭建 微信小程序开发系列二:微信小程序的视图设计 微信小程序开发系列三:微信小程序的调试方法 微信小程序开发系列四:微信小程序 ...

  6. 微信小程序开发系列七:微信小程序的页面跳转

    微信小程序开发系列教程 微信小程序开发系列一:微信小程序的申请和开发环境的搭建 微信小程序开发系列二:微信小程序的视图设计 微信小程序开发系列三:微信小程序的调试方法 微信小程序开发系列四:微信小程序 ...

  7. WordPress版微信小程序开发系列(二):安装使用问答

    自WordPress版微信小程序发布开源以来,受关注的程度超过我原来的想象.这套程序主要面对的用户是wordpress网站的站长,如果wordpress站想在微信的生态圈得到推广,小程序成为一种重要的 ...

  8. 微信小程序开发系列二:微信小程序的视图设计

    大家如果跟着我第一篇文章 微信小程序开发系列一:微信小程序的申请和开发环境的搭建 一起动手,那么微信小程序的开发环境一定搭好了.效果就是能把该小程序的体验版以二维码的方式发送给其他朋友使用. 这个系列 ...

  9. 【微信小程序开发•系列文章六】生命周期和路由

    这篇文章理论的知识比较多一些,都是个人观点,描述有失妥当的地方希望读者指出. [微信小程序开发•系列文章一]入门 [微信小程序开发•系列文章二]视图层 [微信小程序开发•系列文章三]数据层 [微信小程 ...

随机推荐

  1. JDK小Bug汇总

    Java官方的Logger.getGlobal().info无效 无效代码 Logger.getGlobal().info("start"); 解决方案(三选一): Logger. ...

  2. 【整理】认识MSG结构体

    在Windows程序中,消息是由MSG结构体来表示的.MSG结构体的定义如下(参见MSDN): typedef struct tagMSG { HWND hwnd; UINT message; WPA ...

  3. BZOJ1491: [NOI2007]社交网络

    传送门 最短路计数问题.因为数据量非常小($N \leq 100$),所以Floyd随便搞搞就行了. $f[i][j]$表示路径长度,$g[i][j]$表示最短路方案数. 先跑一遍裸的Floyd,然后 ...

  4. C#之数组篇

    大杂烩 一.数组初始化 1.一维数组 String[] str = new String[3] { "1","2","3"};        ...

  5. python学习笔记-(十二)scoket编程基础

    socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. socket起源于Un ...

  6. margin和padding的区别

    目前web2.0已经越来被人们认可,因为喜欢搞web开发的人员不得不硬着头皮去学习web2.0的标准,其中很重要的一条就是新的布局规则,div+css.以前基本上是用table布局的,这种传统的方式简 ...

  7. 11月8日上午Jquery的基础语法、选取元素、操作元素、加事件、挂事件及移除事件

    jquery基础知识 1.jquery文件的引入,所有的js代码要写在下面那段代码下面. <script src="../jquery-1.11.2.min.js">& ...

  8. Apache Shiro 学习记录1

    最近几天在学习Apache Shiro......看了一些大神们的教程.....感觉收获不少.....但是毕竟教程也只是指引一下方向....即使是精品教程,仍然有很多东西都没有说明....所以自己也稍 ...

  9. 【转载】借助GitHub托管你的项目代码

    PS:自己关注博客园有2年之久了,不久前才申请注册账号.GitHub也差不多一年多了,因英语水平刚刚及格,所以去GitHub没有博客园多,也是几个月前才注册了账号,前几天休息时看到 EdisonCho ...

  10. 1 web.xml配置详解

    <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http:// ...