近来在学习Eugene Agafonov编写的《C#多线程编程实战》(译),做些笔记也顺便分享一下^-^
本篇将展示当代码中有多个连接的await方法时程序的实际流程是怎样的。我们将学习如何阅读有await方法的代码,以及理解为什么await调用是异步操作。
using System;
using System.Threading.Tasks;
using System.Threading;
namespace 对连续的异步任务使用await操作符
{
class Program
{
static void Main(string[] args)
{
Task t = AsynchronyWithTPL();
t.Wait();
t = AsynchronyWithAwait();
t.Wait();
Console.ReadKey();
}
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.OnlyOnFaulted | 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.WriteLine("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}",
name, Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsThreadPoolThread);
}
}
}
程序运行的结果如下:
当程序运行时,运行了两个异步操作。我们看到AsynchronyWithAwait方法讲起。它看起来仍然像平常的同步代码,唯一不同只处是使用了两个await声明。最重要的一点是该代码依然是顺序执行的,Async 2任务只有等之前的任务完成后才会开始执行。当阅读上述代码时,程序流很清晰,可以看到什么先运行,什么后运行。但该程序如何是异步程序呢?首先,它不总是异步的。当使用await时如果一个任务已经完成,我们会异步地得到该任务结果。否则,当在代码中看到await声明时,通常的行为是方法执行到该await代码行时将立即返回,并且剩下的代码将会在一个后续操作任务中运行。因此等待操作结果时并没有阻塞程序执行,这是一个异步调用。当AsynchronyWithAwait方法中的代码在执行是,除了在Main方法中调用t.Wait外,我们可以执行任何其他任务。然而,主线程必须等待直到所有异步操作完成,否则主线程完成后所有运行异步操作的后台线程会停止运行。
AsynchronyWithTPL方法模仿了AsynchronyWithAwait的程序流。我们需要一个容器任务来处理所有相互依赖的任务。然后启动任务,给其加了一组后续操作。当该任务完成后,会打印出其结果。然后又启动一个任务,在该任务完成后依次运行更多的后续操作。为了测试对异常的处理,当运行第二个任务时故意抛出一个异常,并打印出异常信息。这组后续操作创建了与第一个方法中一样的程序流。如果用它与await方法比较,可以看到它更容易阅读和理解。唯一的技巧是请记住异步并不总是意味着并行执行。