ASP.NET4.5Web API及非同步程序开发系列(1)
认识非同步程序开发设计模型

从VS2012开始引入的新的非同步程序设计的支持-------async/await设计模型
- 之前的当我们支持非同步作业的时候,往往使用多线程开解决,我们比较熟悉的就是
- 执行者:Thread,ThreadPool (线程和线程池,后者有利于资源的有效利用)
- 非同步的设计模型:Begin方法/End方法,Async事件/Completed事件(主要是异步委托之类的,我在我以前的博文中有写过专题)
- BackgroundWorker控制项
- 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)的更多相关文章
- ASP.NET4.5Web API及非同步程序开发系列3
ASP.NET4.5Web API及非同步程序开发系列(3) 接着上一篇博客的内容做一个补充,正好是一个大哥提出来的,我们看看一个有趣的现象. 请求相关问题的补充: 我们先在Controller中的定 ...
- ASP.NET4.5Web API及非同步程序开发系列
ASP.NET4.5Web API及非同步程序开发系列 认识ASP.NET WEB API 他的前身为WCF WEB API用于协助WCF支持RestFul.现在集成进ASP.NET,正式更名为ASP ...
- ASP.NET4.5Web API及非同步程序开发系列(2)
认识ASP.NET WEB API 他的前身为WCF WEB API用于协助WCF支持RestFul.现在集成进ASP.NET,正式更名为ASP.NET WEB API,ASP.NET Web API ...
- ASP.NET4.5Web API及非同步程序开发系列(3)
接着上一篇博客的内容做一个补充,正好是一个大哥提出来的,我们看看一个有趣的现象. 请求相关问题的补充: 我们先在Controller中的定义一个我们在前一篇博客中已经测试过的方法如下: public ...
- 微信小程序开发系列六:微信框架API的调用
微信小程序开发系列教程 微信小程序开发系列一:微信小程序的申请和开发环境的搭建 微信小程序开发系列二:微信小程序的视图设计 微信小程序开发系列三:微信小程序的调试方法 微信小程序开发系列四:微信小程序 ...
- 微信小程序开发系列七:微信小程序的页面跳转
微信小程序开发系列教程 微信小程序开发系列一:微信小程序的申请和开发环境的搭建 微信小程序开发系列二:微信小程序的视图设计 微信小程序开发系列三:微信小程序的调试方法 微信小程序开发系列四:微信小程序 ...
- WordPress版微信小程序开发系列(二):安装使用问答
自WordPress版微信小程序发布开源以来,受关注的程度超过我原来的想象.这套程序主要面对的用户是wordpress网站的站长,如果wordpress站想在微信的生态圈得到推广,小程序成为一种重要的 ...
- 微信小程序开发系列二:微信小程序的视图设计
大家如果跟着我第一篇文章 微信小程序开发系列一:微信小程序的申请和开发环境的搭建 一起动手,那么微信小程序的开发环境一定搭好了.效果就是能把该小程序的体验版以二维码的方式发送给其他朋友使用. 这个系列 ...
- 【微信小程序开发•系列文章六】生命周期和路由
这篇文章理论的知识比较多一些,都是个人观点,描述有失妥当的地方希望读者指出. [微信小程序开发•系列文章一]入门 [微信小程序开发•系列文章二]视图层 [微信小程序开发•系列文章三]数据层 [微信小程 ...
随机推荐
- 牛顿法|阻尼牛顿法|拟牛顿法|DFP算法|BFGS算法|L-BFGS算法
一直记不住这些算法的推导,所以打算详细点写到博客中以后不记得就翻阅自己的笔记. 泰勒展开式 最初的泰勒展开式,若 在包含 的某开区间(a,b)内具有直到n+1阶的导数,则当x∈(a,b)时,有: ...
- 创建16x16二级hash目录
Hash目录是一种优化文件存储性能的方法.无论是Windows还是Linux,无论是NTFS还是ext4,每个目录下所能容纳的项目数是有限的.并不是不能保存,而是当项目数量过大的时候,会降低文件索引速 ...
- Python Day6
面向对象 概述 面向过程:根据业务逻辑从上到下写垒代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类和封装,让开发"更快更好更强...&qu ...
- Python Day5
模块 模块,用一砣代码实现了某个功能的代码集合. 类似于函数式编程和面向过程编程,函数式编程则完成一个功能,其他代码用来调用即可,提供了代码的重用性和代码间的耦合.而对于一个复杂的功能来,可能需要多个 ...
- debian 缺少固件怎么解决
一般是安装的时候会遇到这个问题.http://www.debian.org/releases/stable/amd64/ch02s02.html.en 解决办法是先下载对应debian版本的firmw ...
- SqlServer 产生随机数
ALTER PROCEDURE [dbo].[usp_RandomNumber] ( , --随机数位数 --随机笔数 ) AS BEGIN DECLARE @T AS TABLE([Random N ...
- SQL Server遍历表的几种方法
在数据库开发过程中,我们经常会碰到要遍历数据表的情形,一提到遍历表,我们第一印象可能就想到使用游标,使用游标虽然直观易懂,但是它不符合面向集合操作的原则,而且性能也比面向集合低.当然,从面向集合操作的 ...
- coredump简介与coredump原因总结
from:http://www.cnblogs.com/doctorqbw/archive/2011/12/21/2295962.html 千兵卫博士 coredump简介与coredump原 ...
- 关于使用 no-js (Modernizr)
最近有些朋友问到:为什么我的网页 code 有 class="no-js" ? <!DOCTYPE html> <html dir="ltr" ...
- 万恶的jar包冲突
搭了个spring+struts2+mybatis的项目架子, 好久不用myEclipse和tomcat了,生疏了好多. 建议还是去百度一些框架整合的博客,直接使用博客里面给的jar包列表里的jar包 ...