运行结果如下所示,需要注意的是,如果在ContinueWith()方法中捕获多个任务产生的异常,那么它的异常类型是AggregateException,具体的异常信息包含在InnerExceptions里面,要注意和InnerException区分。
1.9 并行运行任务本节中主要介绍了两个方法的使用,一个是等待组中全部任务都执行结束的Task.WhenAll()方法,另一个是只要组中一个方法执行结束都执行的Task.WhenAny()方法。
具体使用,如下演示代码所示。
static void Main(string[] args) { // 第一种方式 通过Task.WhenAll 等待所有任务运行完成 var firstTask = new Task<int>(() => TaskMethod("First Task", 3)); var secondTask = new Task<int>(() => TaskMethod("Second Task", 2)); // 当firstTask 和 secondTask 运行完成后 才执行 whenAllTask的ContinueWith var whenAllTask = Task.WhenAll(firstTask, secondTask); whenAllTask.ContinueWith(t => WriteLine($"第一个任务答案为{t.Result[0]},第二个任务答案为{t.Result[1]}"), TaskContinuationOptions.OnlyOnRanToCompletion); firstTask.Start(); secondTask.Start(); Sleep(TimeSpan.FromSeconds(4)); // 使用WhenAny方法 只要列表中有一个任务完成 那么该方法就会取出那个完成的任务 var tasks = new List<Task<int>>(); for (int i = 0; i < 4; i++) { int counter = 1; var task = new Task<int>(() => TaskMethod($"Task {counter}",counter)); tasks.Add(task); task.Start(); } while (tasks.Count > 0) { var completedTask = Task.WhenAny(tasks).Result; tasks.Remove(completedTask); WriteLine($"一个任务已经完成,结果为 {completedTask.Result}"); } ReadLine(); } static int TaskMethod(string name, int seconds) { WriteLine($"任务运行在{CurrentThread.ManagedThreadId}上. 是否为线程池线程:{CurrentThread.IsThreadPoolThread}"); Sleep(TimeSpan.FromSeconds(seconds)); return 42 * seconds; }运行结果如下图所示。
1.10 使用TaskScheduler配置任务执行在Task中,负责任务调度是TaskScheduler对象,FCL提供了两个派生自TaskScheduler的类型:线程池任务调度器(Thread Pool Task Scheduler)和同步上下文任务调度器(Synchronization Scheduler)。默认情况下所有应用程序都使用线程池任务调度器,但是在UI组件中,不使用线程池中的线程,避免跨线程更新UI,需要使用同步上下文任务调度器。可以通过执行TaskScheduler的FromCurrentSynchronizationContext()静态方法来获得对同步上下文任务调度器的引用。
演示程序如下所示,为了延时同步上下文任务调度器,我们此次使用WPF来创建项目。
MainWindow.xaml 代码如下所示。
<Window x:Class="Recipe9.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Recipe9" mc:Ignorable="d" Title="MainWindow"> <Grid> <TextBlock HorizontalAlignment="Left" Margin="44,134,0,0" VerticalAlignment="Top"/> <Button Content="Sync" HorizontalAlignment="Left" Margin="45,190,0,0" VerticalAlignment="Top"/> <Button Content="Async" HorizontalAlignment="Left" Margin="165,190,0,0" VerticalAlignment="Top"/> <Button Content="Async OK" HorizontalAlignment="Left" Margin="285,190,0,0" VerticalAlignment="Top"/> </Grid> </Window>