您的位置:首页 > 编程语言

async/await 异步编程1324ssc.com

2014-10-19 06:44 447 查看

前言

  最近在学习Web Api框架的时候接触到了async/await,这个特性是.NET 4.5引入的,由于之前对于异步编程不是很了解,所以花费了一些时间学习一下相关的知识,并整理成这篇博客,如果在阅读的过程中发现不对的地方,欢迎大家指正。

同步编程与异步编程

  通常情况下,我们写的C#代码就是同步的,运行在同一个线程中,从程序的第一行代码到最后一句代码顺序执行。而异步编程的核心是使用多线程,通过让不同的线程执行不同的任务,实现不同代码的并行运行。

前台线程与后台线程

  关于多线程,早在.NET2.0时代,基础类库中就提供了Thread实现。默认情况下,实例化一个Thread创建的是前台线程,只要有前台 线程在运行,应用程序的进程就一直处于运行状态,以控制台应用程序为例,在Main方法中实例化一个Thread,这个Main方法就会等待Thread 线程执行完毕才退出。而对于后台线程,应用程序将不考虑其是否执行完毕,只要应用程序的主线程和前台线程执行完毕就可以退出,退出后所有的后台线程将被自 动终止。来看代码应该更清楚一些:

前台线程&后台线程
  运行上面的代码,可以看到DoRun2方法的打印信息“这是后台线程调用”将不会被显示出来,因为应用程序执行完主线程和前台线程后,就自动退 出了,所有的后台线程将被自动终止。这里后台线程设置了等待1.5s,假如这个后台线程比前台线程或主线程提前执行完毕,对应的信息“这是后台线程调用” 将可以被成功打印出来。

Task

  .NET 4.0推出了新一代的多线程模型Task。async/await特性是与Task紧密相关的,所以在了解async/await前必须充分了解Task的使用。这里将以一个简单的Demo来看一下Task的使用,同时与Thread的创建方式做一下对比。

Task的使用
  首先,必须明确一点是Task启动的线程是后台线程,不过可以通过在Main方法中调用task.Wait()方法,使应用程序等待task执 行完毕。Task与Thread的一个重要区分点是:Task底层是使用线程池的,而Thread每次实例化都会创建一个新的线程。这里可以通过这段代码 做一次验证:

Task底层使用线程池
  运行代码,可以看到DoRun1()方法每次的Thread Id都是不同的,而DoRun2()方法的Thread Id是重复出现的。我们知道线程的创建和销毁是一个开销比较大的操作,Task.Run()每次执行将不会立即创建一个新线程,而是到CLR线程池查看是 否有空闲的线程,有的话就取一个线程处理这个请求,处理完请求后再把线程放回线程池,这个线程也不会立即撤销,而是设置为空闲状态,可供线程池再次调度, 从而减少开销。

Task<TResult>

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

-

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

帝尊在线平台

  Task<TResult>

是Task的泛型版本,这两个之间的最大不同是Task<TResult>可以有一个返回值,看一下代码应该一目了然:

Task<TResult>的使用
  Task<TResult>的实例对象有一个Result属性,当在Main方法中调用task.Result的时候,将等待task执行完毕并得到返回值,这里的效果跟调用task.Wait()是一样的,只是多了一个返回值。

async/await 特性

  经过前面的铺垫,终于迎来了这篇文章的主角async/await,还是先通过代码来感受一下这两个特性的使用。

async/await 用法
  首先来看一下async关键字。async用来修饰方法,表明这个方法是异步的,声明的方法的返回类型必须为:void或Task或 Task<TResult>。返回类型为Task的异步方法中无需使用return返回值,而返回类型为 Task<TResult>的异步方法中必须使用return返回一个TResult的值,如上述Demo中的异步方法返回一个int。

  再来看一下await关键字。await必须用来修饰Task或Task<TResult>,而且只能出现在已经用async关键字修饰的异步方法中。

  通常情况下,async/await必须成对出现才有意义,假如一个方法声明为async,但却没有使用await关键字,则这个方法在执行的 时候就被当作同步方法,这时编译器也会抛出警告提示async修饰的方法中没有使用await,将被作为同步方法使用。了解了关键字 async\await的特点后,我们来看一下上述Demo在控制台会输入什么吧。

   不使用async\await
  对比两种方法,是不是async\await关键字的原理其实就是通过使用一个线程完成异步调用吗?答案是否定的。async关键字表明可以在 方法内部使用await关键字,方法在执行到await前都是同步执行的,运行到await处就会挂起,并返回到Main方法中,直到await标记的 Task执行完毕,才唤醒回到await点上,继续向下执行。更深入点的介绍可以查看文章末尾的参考文献。

async/await 实际应用

  微软已经对一些基础类库的方法提供了异步实现,接下来将实现一个例子来介绍一下async/await的实际应用。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Threading;
using System.Threading.Tasks;
using System.Net;

namespace TestApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("开始获取博客园首页字符数量");
Task<int> task1 = CountCharsAsync("http://www.cnblogs.com");
Console.WriteLine("开始获取百度首页字符数量");
Task<int> task2 = CountCharsAsync("http://www.baidu.com");

Console.WriteLine("Main方法中做其他事情");

Console.WriteLine("博客园:" + task1.Result);
Console.WriteLine("百度:" + task2.Result);
}

static async Task<int> CountCharsAsync(string url)
{
WebClient wc = new WebClient();
string result = await wc.DownloadStringTaskAsync(new Uri(url));
return result.Length;
}
}
}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: