http://www.cnblogs.com/fzrain/p/3545810.html

前言

C#5.0最重要的改进,就是提供了更强大的异步编程。C#5.0仅增加两个新的关键字:async和await。

使用异步编程,方法调用是在后台运行(通常在线程或任务的帮助下),并且不会阻塞调用线程。

本文将介绍3种不同模式的异步编程:异步模式,基于事件的异步模式和新增加的基于任务的异步编程模式(TAP)。TAP是利用async和await关键字来实现的。通过这里的比较,将认识到新的增加的基于任务的异步模式的真正优势。

假设情景:我们需要进行一个耗时操作(这里使用webclient对象下载百度首页代码),接下来通过同步以及上面谈到的3种异步模式实现。

同步调用

创建一个控制台应用程序:

static void Main(string[] args)
{
WebClient client = new WebClient();
client.Encoding = Encoding.UTF8;
string resp = client.DownloadString("http://www.baidu.com");
Console.WriteLine(resp);
//todo:其他操作
Console.ReadKey();
}

运行上述代码,在执行DownloadString的时候,主线程会被阻塞,如果有用户界面,那么用户体验肯定不好,而DownloadString这个方法的执行速度取决于网速等原因,所以在这种情况下,使用异步调用就非常有必要了。

异步模式

实现异步模式定义BeginXXX方法和EndXXX方法。例如,如果有一个同步方法DownloadString,异步方法将转化成2个方法BeginDownloadString和EndDownloadString。BeginXXX异步方法接受同步方法的所有输入参数以及一个AsyncCallback参数(一个委托,在异步方法执行完调用),返回一个IAsyncResult对象,用于验证调用是否完成,EndXXX异步方法使用同步方法的所有输出参数,并以同步方法的返回类型来返回结果(简单来说就是获取异步执行方法的返回值,如果在异步执行的方法还没结束,则阻塞主线程直到异步方法执行结束)。

WebClient并没有异步模式的实现,但是可以用HttpWebRequest类来代替(该类有BeginGetResponse和EndGetResponse):

static void Main(string[] args)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://www.baidu.com");
req.BeginGetResponse(ar =>
{
HttpWebResponse resp = (HttpWebResponse)req.EndGetResponse(ar); Console.WriteLine(resp.ContentType );
//todo:其他操作
}, null);
Console.WriteLine("这是主线程"); Console.ReadKey();
}

如果非要用WebClient来实现异步模式,则需要用委托(只给代码不多做介绍了):

static void Main(string[] args)
{
//定义委托实例
Func<string, string> downloadString = url =>
{
WebClient client = new WebClient();
client.Encoding = Encoding.UTF8;
return client.DownloadString(url); };
//异步执行委托
downloadString.BeginInvoke("http://www.baidu.com", ar =>
{
string resp = downloadString.EndInvoke(ar);
Console.WriteLine(resp);
},null);
Console.WriteLine("这是主线程"); Console.ReadKey();
}

异步模式的优势是使用委托就能实现异步编程。不用改变程序的行为。也不会阻塞界面的操作。但是,使用异步模式的过程是非常复杂的(尤其在winform或wpf中涉及更新界面操作时)。幸运的是,.NET2.0推出了基于事件的异步模式,能很轻松更新界面。

基于事件的异步模式

基于事件的异步模式定义了一个带有“Async”后缀的方法。例如:对于同步方法DownloadString,WebClient类提供一个异步变体方法DownloadStringAsync。异步方法完成后,不是定义被调用的委托,而是定义事件。当异步方法DownloadStringAsync完成后,会直接调用DownloadStringCompleted事件,实现方式和上面差不多。但这时可以直接访问界面UI元素。

在这里我们换一个winform项目(界面只包含一个label和一个button),点击button更新label值:

private void button1_Click(object sender, EventArgs e)
{ WebClient client = new WebClient();
client.Encoding = Encoding.UTF8;
client.DownloadStringCompleted += (sender1, e1) =>
{
string resp = e1.Result;
label1.Text = resp.Substring();
};
client.DownloadStringAsync(new Uri("http://www.baidu.com")); }

基于事件的异步模式的优势在于易于使用。但是,如果在自定义类中实现这个模式,就没那么简单了。方法还是有的,可以使用BackgroundWorker类来实现异步调用同步方法。BackgroundWorker类实现了基于事件的异步模式。还有,这种模式与同步方法调用相比,顺序颠倒了。调用异步方法之前,需要定义这个方法完成时发生什么。因此,下面进入异步编程的新世界,利用async和await关键字。

基于任务的异步模式(推荐使用)

这次先看代码:

private async  void button1_Click(object sender, EventArgs e)
{ WebClient client = new WebClient();
client.Encoding = Encoding.UTF8; string resp=await client.DownloadStringTaskAsync("http://www.baidu.com");
label1.Text = resp.Substring(); }

在.NET4.5中,更新了WebClient类,提供了基于任务的异步模式。该模式定义了一个带有“Async”后缀的方法,并返回一个Task类型。由于WebClient类已经提供了一个带有“Async”后缀的方法,因此新方法名为DownloadStringTaskAsync。

DownloadStringTaskAsync方法声明返回Task<string>类型。但是,不需要为DownloadStringTaskAsync方法返回的结果声明一个Task<string>类型的变量。只要声明一个string类型的变量,并且使用await关键字。await关键字不会阻塞完成其他任务的线程。当DownloadStringTaskAsync方法完成其后台处理后,UI线程会继续并从后台线程中获得结果,赋值给resp。同时,在await关键字的下一行代码会继续执行。

现在,代码简单多了。没有阻塞,也不需要手工切回UI线程,这些都是自动实现的。代码顺序也和惯用的同步编程基本上一样了。

转换异步模式

并非.NET FrameWork的所有类在.NET 4.5中都引入了新的异步方法,有些类还是只提供了BeginXXX和EndXXX方法的异步模式,那怎么办?

我们以上面用到的HttpWebResponse为例(假设它没有新的异步方法):Task类型的泛型参数Task<WebResponse>,定义了调用方法的返回值类型,FromAsync方法的前两个参数是委托类型,传入对应的BeginXXX和EndXXX,由于我们没有输入参数,因而也没有输入参数的对象状态,所有是null,代码如下:

private async   void button1_Click(object sender, EventArgs e)
{
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://www.baidu.com"); HttpWebResponse resp = (HttpWebResponse)await Task<WebResponse>.Factory.FromAsync(req.BeginGetResponse, req.EndGetResponse, null); }

之前一直对这块比较模糊,今天学了总结下,关于异步编程以及多线程还有好多东西要学,也很复杂,本文也就简单介绍到这了

C#基础——谈谈.NET异步编程的演变史的更多相关文章

  1. 基础篇:异步编程不会?我教你啊!CompeletableFuture

    前言 以前需要异步执行一个任务时,一般是用Thread或者线程池Executor去创建.如果需要返回值,则是调用Executor.submit获取Future.但是多个线程存在依赖组合,我们又能怎么办 ...

  2. 【译】异步JavaScript的演变史:从回调到Promises再到Async/Await

    我最喜欢的网站之一是BerkshireHathaway.com--它简单,有效,并且自1997年推出以来一直正常运行.更值得注意的是,在过去的20年中,这个网站很有可能从未出现过错误.为什么?因为它都 ...

  3. Node.js之异步编程

    > 文章原创于公众号:程序猿周先森.本平台不定时更新,喜欢我的文章,欢迎关注我的微信公众号. ![file](https://img2018.cnblogs.com/blog/830272/20 ...

  4. 学习Promise异步编程

    JavaScript引擎建立在单线程事件循环的概念上.单线程( Single-threaded )意味着同一时刻只能执行一段代码.所以引擎无须留意那些"可能"运行的代码.代码会被放 ...

  5. Java 异步编程的几种方式

    前言 异步编程是让程序并发运行的一种手段.它允许多个事情同时发生,当程序调用需要长时间运行的方法时,它不会阻塞当前的执行流程,程序可以继续运行,当方法执行完成时通知给主线程根据需要获取其执行结果或者失 ...

  6. C#基础系列——异步编程初探:async和await

    前言:前面有篇从应用层面上面介绍了下多线程的几种用法,有博友就说到了async, await等新语法.确实,没有异步的多线程是单调的.乏味的,async和await是出现在C#5.0之后,它的出现给了 ...

  7. async 与 await异步编程活用基础

    [本文转自:http://www.cnblogs.com/x-xk/archive/2013/06/05/3118005.html  作者:肅] 好久没写博客了,时隔5个月,奉上一篇精心准备的文章,希 ...

  8. async And await异步编程活用基础

    原文:async And await异步编程活用基础 好久没写博客了,时隔5个月,奉上一篇精心准备的文章,希望大家能有所收获,对async 和 await 的理解有更深一层的理解. async 和 a ...

  9. C#复习笔记(5)--C#5:简化的异步编程(异步编程的基础知识)

    异步编程的基础知识 C#5推出的async和await关键字使异步编程从表面上来说变得简单了许多,我们只需要了解不多的知识就可以编写出有效的异步代码. 在介绍async和await之前,先介绍一些基础 ...

随机推荐

  1. KVO的使用

    KVO的使用 KVO是一种设计模式,名为观察者. addObserver:forKeyPath:options:context: 通知其他对象的方法,这个方法在NSObject中就已经申明了,也就是说 ...

  2. GridControl控件的数据显示的样式控制(转)

    如上两图所示,Dev列表控件GridControl默认的格式并没有渐变变色效果,显示的日期数据,也是“yyyy-MM-dd”的格式,而非“yyyy-MM-dd HH:mm:ss”即使对于后面有长格式的 ...

  3. 给Apache增加SSI支持(shtml的奥秘)

    什么是SSI? SSI是英文Server Side Includes的缩写,翻译成中文就是服务器端包含的意思.从技术角度上说,SSI就是在HTML文件中,可以通过注释行调用的命令或指针.SSI具有强大 ...

  4. Openwrt LuCI模块练习详细步骤

    前言 又到了成胖子^_^每周一博的时间了.最近在学习openwrt luci方面的知识,为了贯穿整个知识体系,练习题目为: 通过页面配置周期性地往/tmp/addtest文件写入内容和时间戳 1.在w ...

  5. #include <NOIP2008 Junior> 双栈排序 ——using namespace wxl;

    题目描述 Tom最近在研究一个有趣的排序问题.如图所示,通过2个栈S1和S2,Tom希望借助以下4种操作实现将输入序列升序排序. 操作a 如果输入序列不为空,将第一个元素压入栈S1 操作b 如果栈S1 ...

  6. Dictionary(HashMap)的实现

    什么是哈希表? 哈希表(Hash table,也叫散列表),是根据key而直接进行访问的数据结构.也就是说,它通过把key映射到表中一个位置来访问记录,以加快查找的速度.这个映射函数叫做散列函数,存放 ...

  7. 用c/c++混合编程方式为ios/android实现一个自绘日期选择控件(一)

    本文为原创,如有转载,请注明出处:http://www.cnblogs.com/jackybu 前言 章节: 1.需求描述以及c/c++实现日期和月历的基本操作 2.ios实现自绘日期选择控件 3.a ...

  8. Java 嵌套解析 json

    1.首先需要安装org.json.jar 2.类JSONObject用于创建一个json对象.其中的JSONObject.put(KEY, VALUE)用于向其中添加条目 3.JSONObject.g ...

  9. UESTC 1227 & POJ 3667 Hotel

    非常细腻的线段树题目啊,后来还是有个细节写错了,查了一个晚上..就不分析了. 代码: #include <iostream> #include <cstdio> #includ ...

  10. POJ 2142 The Balance【扩展欧几里德】

    题意:有两种类型的砝码,每种的砝码质量a和b给你,现在要求称出质量为c的物品,要求a的数量x和b的数量y最小,以及x+y的值最小. 用扩展欧几里德求ax+by=c,求出ax+by=1的一组通解,求出当 ...