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

从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. Neural Style学习3——操作

    Basic usage: th neural_style.lua -style_image <image.jpg> -content_image <image.jpg> Ope ...

  2. [分类算法] :朴素贝叶斯 NaiveBayes

    1. 原理和理论基础(参考) 2. Spark代码实例: 1)windows 单机 import org.apache.spark.mllib.classification.NaiveBayes im ...

  3. a版本冲刺第八天

    队名:Aruba   队员: 黄辉昌 李陈辉 林炳锋 鄢继仁 张秀锋 章  鼎 学号 昨天完成的任务 今天做的任务 明天要做的任务 困难点 体会 408 继续学习第一行代码至第四章,阅读队友代码 着手 ...

  4. iOS - 在工程中试玩状态模式

    做了一个项目,项目中一个藏品详情界面针对不同用户,和用户所处于的状态的不同,展示的效果和操作的权限都会不同.想到了状态模式,从来没有用过,赶紧学一下然后用一用.期待兴奋 看了这么多的博客,终于找到一个 ...

  5. Architectural Model - SNMP Tutorial

    30.3 Architectural Model Despite the potential disadvantages, having TCP/IP management software oper ...

  6. 工作中的一些JS--为网页动态添加元素,类似于邮箱添加联系人的功能

    项目中要解决一个为下拉框动态添加选项的问题,之前从网上搜到结果,写个JS函数 //先新建元素,并添加属性 var option = document.createElement("optio ...

  7. 线程GCD

    #import "ViewController.h" @interfaceViewController () @end @implementation ViewController ...

  8. 如何刷新或清除HttpURLConnection的连接缓存

    项目需要定期与远程服务器同步数据,基于如下代码: URL url = new URL("http://test.com/sales/info"); connection = (Ht ...

  9. zabbix3.0.4 邮件告警详细配置

    sendEmail是一个轻量级,命令行的SMTP邮件客户端.如果你需要使用命令行发送邮件,那么sendEmail是非常完美的选择:使用简单并且功能强大.这个被设计用在php.bash perl和web ...

  10. mysql优化记录

    老板反应项目的反应越来越慢,叫优化一下,顺便学习总结一下mysql优化. 不同引擎的优化,myisam读的效果好,写的效率差,使用场景 非事务型应用只读类应用空间类应用 Innodb的特性,innod ...