这篇文章主要介绍一下C# 5.0语法中的新组合:async 和 await。我这里主要会写一些基本的注意要点,但是不会过于仔细。
首先,必须要说明的是async和await是一对,要使用await关键字,那么函数前必须要有async前缀;这对语法糖不能使用在catch、finally、lock、unsafe代码中;且函数参数不能有out或者ref修饰;异步函数必须返回Task或者Task<T>类型。
比较典型的使用情况是使用await Task.Delay(2000),来让线程等待2秒钟(但是不同于Thread.Sleep(2000),后者会阻塞线程,而前者会释放线程回到线程池中,进而节省线程资源)。
async static Task<string> GetInfoAsync(string name)
{
await Task.Delay(2000);
return string.Format("Task {0} 运行在线程Id为 {1}的线程上。是否是线程池线程: {2}",
name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
}
GetInfoAsync函数使用了前缀async,主要是为了使得函数体内可以使用await Task.Delay()。当然,你也可以使用像上篇中讲到的用:
Task delay = Task.Dealy(2000);
Return delay.ContinueWith(t=>return string.Format("Task {0} 运行在线程Id为 {1}的线程上。是否是线程池线程: {2}",name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); );
可以明显的感受到,前者的代码量少了一些,而且逻辑也更加简单。但是,作为一个新手,我还是觉得使用了Async后,本应该返回一个Task<String>的,现在返回了一个String,这个有点奇怪。我就简单地理解为只要有async作为Task<T>的前缀,那么就要返回T类型数据;如果是async作为Task的前缀,那么不用返回。同样,如果要得到返回的T类型结果,那么在调用该函数时必须加前缀await;反之,则只是返回一个Task<T>。这里如果有不对还请大师们指点。
一、首先来看传统的Task TPL方法:
static Task AsynchronyWithTPL()
{
Task<string> t = GetInfoAsync("Task 1");
Task t2 = t.ContinueWith(task => Console.WriteLine(t.Result), TaskContinuationOptions.NotOnFaulted);
Task t3 = t.ContinueWith(task => Console.WriteLine(t.Exception.InnerException), TaskContinuationOptions.OnlyOnFaulted);
return Task.WhenAny(t2, t3);
}
调用函数:
Task t = AsynchronyWithTPL();
t.Wait();
简单说明一下,主线程中调用的Wait()是能够自动启动相关的Task的。AsynchronyWithTPL中里面用了Task.WhenAny()方法来返回一个Task。注意下t2和t3的状态条件!
二、再来看一下Await的使用
async static Task AsynchronyWithAwait()
{
try
{
string result = await GetInfoAsync("Task 2"); //有了await则返回String类型。
Console.WriteLine(result);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
async static Task<string> GetInfoAsync(string name)
{
await Task.Delay(2000);
throw new Exception("Boom!");
return string.Format("Task {0} 运行在线程Id为 {1}的线程上。是否是线程池线程: {2}",
name, Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread);
}
不同于以往我们说的,不同线程是无法处理其他线程抛出的异常。这里我们在
AsynchronyWithAwait
函数中直接使用了我们通常熟悉的Try Catch方式,主线程(AsynchronyWithAwait 其实运行在主线程上)正确的捕获了由线程池线程引发的异常。