为了简化异步编程,.net 4.5引入了两个新的关键字async和await,使得异步编程大大简化,代码可读性和可维护性成倍提高,不过代价总是有,就是程序员必须明白.net 4.0中引入的Task概念,没有Task基础知识,使用async和await会感觉一头雾水。就好像不明白delegate、generic而去强行使用LINQ一样。
Task的出现是为了使多线程编程变得简单,Task和Thread还不是一回事,Task由TaskManager管理,一个Task会执行在一个Thread上,但是如果没有足够的Thread,TaskManager会暂时挂起这个Task,直到有足够的Thread资源为止。Task的另外一个好处是TaskManager会把执行这些Task的Thread自动的分配在不同的Core上,就是所谓的并行编程,拿我的i7来说,有4个物理Core。如果有4个Task,那么.net TaskManager会在4个Core上建立4个独立的Thread,然后把4个Task放到这4个Thread运行,有点像绕口令,这都是MSDN说的,我也不知道真假,也没测过,估计是真的吧
.net 4.0以后新出现的并行编程、异步编程框架都是基于Task,前段时间出现的Reactive Extention(Rx)也是如此。async和await和Rx有一些功能重复,Rx先按下不表,以后再说。
用一个例子介绍一下async和await,这是前段时间我再给windows phone 8写app时的时候实际用到的。在没有async和await之前,类似的win phone代码简直乱得一团糟,写完了都不想再看第二眼(很好奇iPhone和安卓是怎样处理类似情况的?),现在写完了很想再看第二眼:
project: 根据email and password得到用户权限userRight,再根据userRight列出这个user能看到的所有电影movieList,
先把代码放出,一个是同步,一个是async + await 异步
static void Main(string[] args)
{
Console.WriteLine("main thread started..");
getUserMoves("me@hotmail.com", "password");
getUserMoviesTaskAsync("me@hotmail.com","password");
Console.WriteLine("waiting for main thread to end");
Console.ReadLine();
}
private static void getUserMoves(string email, string password)
{
WebClient wb = new WebClient();
string userRight = wb.DownloadString(email + password);
string moviesUserCanWatch = wb.DownloadString(userRight);
Console.WriteLine(moviesUserCanWatch);
}
private static async void getUserMoviesTaskAsync(string email, string password)
{
WebClient wb = new WebClient();
string userRight = await wb.DownloadStringTaskAsync(email + password);
string moviesUserCanWatch = await wb.DownloadStringTaskAsync(userRight);
Console.WriteLine(moviesUserCanWatch);
}
第一个
getUserMoves("me@hotmail.com", "password");
是同步代码,也就是说除非里面所有步骤都执行完毕,否则主线程会被block,你永远也看不到“waiting for main thread to end”
第二个
getUserMoviesTaskAsync("me@hotmail.com","password");
是异步代码,程序会输出:
main thread started..
waiting for main thread to end
然后才会输出 downloaded string,主线程不会被block
写法极其相似,达到同样效果,但是一个是同步,一个是异步,异步的代码当然可以有其它多种实现方法,但是.net 4.5的这种async + await写法是最简洁的
大概讲一下原理:
一旦一个函数被前面有async关键字,比如:
async void getUserMoviesTaskAsync
那么.net 在执行到这个函数的时候会像对待普通的函数一样去运行这个函数内的语句,同时主线程被block,等待getUserMoviesTaskAsync结束
在执行 getUserMoviesTaskAsync 函数内的语句的时候,一旦遇到await关键字,比如:
await wb.DownloadStringTaskAsync(email + password);
.net会建立另外的一个Thread去运行wb.DownloadStringTaskAsync(email + password)
同时把运行权交还给调用getUserMoviesTaskAsync函数的Object,在这里,就是Main(string[] args) {}啦,于是main继续运行输出:
“waiting for main thread to end”
当wb.DownloadStringTaskAsync(email + password)运行结束,.net会把运行权切换到getUserMoviesTaskAsync函数,让它继续运行,直到碰到下一个await,这时再次把运行权切换到主线程,然后再返回getUserMoviesTaskAsync函数,直到getUserMoviesTaskAsync函数运行结束,返回主线程