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. 多线程基础(三)NSThread基础

    3.多线程基础 NSThread的基本使用   如何使用NSThread创建新线程 创建线程之后是默认不执行的状态 创建三个线程:   通过name属性区别这些线程 接下来就是通过设置线程的优先级来设 ...

  2. MongoDB 初识1: 启动MongoDB

    Mongodb可作为网络服务器运行,客户端可连接到该服务器并执行操作. 1. 首先,用户必须下载Mongodb并解压,运行mongod命令启动实例 下载地址:http://www.mongodb.or ...

  3. bsearch的溢出问题

    在java中为了避免 low+high溢出,可以用无符号右移:正数高位补0,负数高位补1 int mid = (low + high) >>> 1; 如果是在c++中,那么需要先转换 ...

  4. JavaScript Patterns 4.5 Immediate Functions

    The immediate function pattern is a syntax that enables you to execute a function as soon as it is d ...

  5. JavaScript Patterns 2.11 Writing Comments

    Document all functions, their arguments and return values, and also any interesting or unusual algor ...

  6. sql server 导出的datetime结果 CAST(0x00009E0E0095524F AS DateTime) 如何向mysql,oracle等数据库进行转换

    1. 处理 sql server 导出的 datetime 类型的字段 在进行sql server向mysql等其他数据进行迁移数据时,会发现使用sql server导出的datetime类型的结果是 ...

  7. PL/SQL之--游标

    一.游标简介 在PL/SQL中执行SELECT.INSERT.DELETE和UPDATE语句时,ORACLE会在内存中为其分配上下文区(Context Area),也称为缓冲区.游标是指向该区的一个指 ...

  8. emacs下安装pip

    Ubuntu13.10下安装pip的方法   $ sudo apt-get install python-pip python-dev build-essential  $ sudo pip inst ...

  9. 【DPDK】虚拟机开发环境配置

    DPDK介绍见:www.dpdk.org 本文介绍的步骤基本适用于dpdk 1.7.0 - dpdk 2.0.0 各版本.只是setup.sh显示的菜单有一些小的不同:同样的,也适用于ubuntu更高 ...

  10. 动手学习TCP:服务端状态变迁

    上一篇文章介绍了TCP状态机,并且通过实验了解了TCP客户端正常的状态变迁过程. 那么,本篇文章就一起看看TCP服务端的正常状态变迁过程 服务端状态变迁 根据上一篇文章中的TCP状态变迁图,可以得到服 ...