C# 使用C# 5.0(在lambda表达式中使用await操作符)
在lambda表达式中使用await操作符
本节将展示如何在lambda表达式中使用await。我们将编写一个使用了await的匿名方
法,并且获取异步执行该方法的结果。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace 在lambda表达式中使用await操作符
{
internal class Program
{
static void Main(string[] args)
{
Task t = AsynchronousProcessing();
t.Wait();
}
async static Task AsynchronousProcessing()
{
Func<string, Task<string>> asyncLambda = async name =>
{
await Task.Delay(TimeSpan.FromSeconds(2));
return string.Format("Task {0} is running on a thread id {1}.Is thread pool thread:{2}", name,
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsThreadPoolThread);
};
string result = await asyncLambda("asyn lambda");
Console.WriteLine(result);
}
}
}
运行结果
Task asyn lambda is running on a thread id 4.Is thread pool thread:True
工作原理
首先,由于不能在Main方法中使用async,我们将异步函数移到了Asynchronous-
Processing方法中。然后使用async关键字声明了一个lambda表达式。由于任何lambda表达
式的类型都不能通过lambda自身来推断,所以不得不显式向C#编译器指定它的类型。在本
例中,该类型说明该[ambda表达式接受一个字符串参数,并返回一个Task对象。
接着,我们定义了lambda表达式体。有个问题是该方法被定义为返回一个Task
对象,但实际上返回的是字符串,却没有编译错误!这是因为C#编译器自动产生一个任务
并返回给我们。
最后一步是等待异步lambda表达式执行并打印出结果。
对连续的异步任务使用await操作符
本节将展示当代码中有多个连续的await方法时程序的实际流程是怎样的。我们将学习如何阅读有await方法的代码,以及理解为什么awatt调用是异步操作。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Dynamic;
namespace 对连续的异步任务使用await操作符
{
internal class Program
{
static void Main(string[] args)
{
Task t = AsynchronyWithTPL();
t.Wait();
t = AsynchronyWithAwait();
t.Wait();
}
static Task AsynchronyWithTPL()
{
var containerTask = new Task(() =>
{
Task<string> t = GetInfoAsync("TPL 1");
t.ContinueWith(task =>
{
Console.WriteLine(t.Result);
Task<string> t2 = GetInfoAsync("TPL 2");
t2.ContinueWith(innerTask =>
Console.WriteLine(innerTask.Result),
TaskContinuationOptions.NotOnFaulted |
TaskContinuationOptions.AttachedToParent);
t2.ContinueWith(innerTask =>
Console.WriteLine(innerTask.Exception.InnerException),
TaskContinuationOptions.OnlyOnCanceled | TaskContinuationOptions.AttachedToParent);
},
TaskContinuationOptions.NotOnFaulted |
TaskContinuationOptions.AttachedToParent);
t.ContinueWith(task =>
Console.WriteLine(t.Exception.InnerException),
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.AttachedToParent);
});
containerTask.Start();
return containerTask;
}
async static Task AsynchronyWithAwait()
{
try
{
string result = await GetInfoAsync("Async 1");
Console.WriteLine(result);
result = await GetInfoAsync("Async 2");
Console.WriteLine(result);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
async static Task<string>GetInfoAsync(string name)
{
Console.Write("Task {0} started!,name");
await Task.Delay(TimeSpan.FromSeconds(2));
if (name == "TPL 2")
throw new Exception("Boom!");
return string.Format("Task {0} is running on a thread id {1}. Is thread pool thread:{2}",
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsThreadPoolThread);
}
}
}
运行结果
Task {0} started!,nameSystem.FormatException: 索引(从零开始)必须大于或等于零,且小于参数列表的大小。
在 System.Text.StringBuilder.AppendFormatHelper(IFormatProvider provider, String format, ParamsArray args)
在 System.String.FormatHelper(IFormatProvider provider, String format, ParamsArray args)
在 System.String.Format(String format, Object arg0, Object arg1)
在 对连续的异步任务使用await操作符.Program.<GetInfoAsync>d__3.MoveNext() 位置 G:\C# 多线程编程实战\使用C#5.0\对连续的异步任务使用await操作符\Program.cs:行号 73
Task {0} started!,nameSystem.FormatException: 索引(从零开始)必须大于或等于零,且小于参数列表的大小。
在 System.Text.StringBuilder.AppendFormatHelper(IFormatProvider provider, String format, ParamsArray args)
在 System.String.FormatHelper(IFormatProvider provider, String format, ParamsArray args)
在 System.String.Format(String format, Object arg0, Object arg1)
在 对连续的异步任务使用await操作符.Program.<GetInfoAsync>d__3.MoveNext() 位置 G:\C# 多线程编程实战\使用C#5.0\对连续的异步任务使用await操作符\Program.cs:行号 73
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
在 对连续的异步任务使用await操作符.Program.<AsynchronyWithAwait>d__2.MoveNext() 位置 G:\C# 多线程编程实战\使用C#5.0\ 对连续的异步任务使用await操作符\Program.cs:行号 56
工作原理
当程序运行时,与上节一样运行了两个异步操作。然而这次从AsynchronyWithAwait
方法讲起。它看起来仍然像平常的同步代码,唯一不同之处是使用了两个await声明。最
重要的一点是该代码依然是顺序执行的,Async2任务只有等之前的任务完成后才会开始执
行。当阅读该代码时,程序流很清晰,可以看到什么先运行,什么后运行。但该程序如何
是异步程序呢?首先,它不总是异步的。当使用awalt时如果一个任务已经完成,我们会
异步地得到该任务结果。否则,当在代码中看到await声明时,通常的行为是方法执行到
该await代码行时将立即返回,并且剩下的代码将会在一个后续操作任务中运行。因此等
待操作结果时并没有阻塞程序执行,这是一个异步调用。当AsynchronyWithAwait方法中的代码在执行时,除了在Main方法中调用t.Wait外,我们可以执行任何其他任务。然而,
主线程必须等待直到所有异步操作完成,否则主线程完成后所有运行异步操作的后台线程
会停止运行。
AsynchronyWithTPL方法模仿了AsynchronyWithAwait的程序流。我们需要一个容器任
务来处理所有相互依赖的任务然后启动主任务,给其加了一组后续操作当该任务完成
后,会打印出其结果。然后又启动了一个任务,在该任务完成后会依次运行更多的后续操
作为了测试对异常的处理,当运行第二个任务时故意抛出一个异常,并打印出异常信息
这组后续操作创建了与第一个方法中一样的程序流。如果用它与await方法比较,可以看到
它更容易阅读和理解。唯一的技巧是请记住异步并不总是意味着并行执行。
更多推荐



所有评论(0)