首页 > Python基础教程 >
-
C#教程之C#多线程编程系列(五)- 使用任务并行(2)
运行结果如下所示,需要注意的是,如果在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" Height="450" Width="800"> <Grid> <TextBlock Name="ContentTextBlock" HorizontalAlignment="Left" Margin="44,134,0,0" VerticalAlignment="Top" Width="425" Height="40"/> <Button Content="Sync" HorizontalAlignment="Left" Margin="45,190,0,0" VerticalAlignment="Top" Width="75" Click="ButtonSync_Click"/> <Button Content="Async" HorizontalAlignment="Left" Margin="165,190,0,0" VerticalAlignment="Top" Width="75" Click="ButtonAsync_Click"/> <Button Content="Async OK" HorizontalAlignment="Left" Margin="285,190,0,0" VerticalAlignment="Top" Width="75" Click="ButtonAsyncOK_Click"/> </Grid> </Window>
MainWindow.xaml.cs 代码如下所示。
/// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } // 同步执行 计算密集任务 导致UI线程阻塞 private void ButtonSync_Click(object sender, RoutedEventArgs e) { ContentTextBlock.Text = string.Empty; try { string result = TaskMethod().Result; ContentTextBlock.Text = result; } catch (Exception ex) { ContentTextBlock.Text = ex.InnerException.Message; } } // 异步的方式来执行 计算密集任务 UI线程不会阻塞 但是 不能跨线程更新UI 所以会有异常 private void ButtonAsync_Click(object sender, RoutedEventArgs e) { ContentTextBlock.Text = string.Empty; Mouse.OverrideCursor = Cursors.Wait; Task<string> task = TaskMethod(); task.ContinueWith(t => { ContentTextBlock.Text = t.Exception.InnerException.Message; Mouse.OverrideCursor = null; }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, TaskScheduler.FromCurrentSynchronizationContext()); } // 通过 异步 和 FromCurrentSynchronizationContext方法 创建了线程同步的上下文 没有跨线程更新UI private void ButtonAsyncOK_Click(object sender, RoutedEventArgs e) { ContentTextBlock.Text = string.Empty; Mouse.OverrideCursor = Cursors.Wait; Task<string> task = TaskMethod(TaskScheduler.FromCurrentSynchronizationContext()); task.ContinueWith(t => Mouse.OverrideCursor = null, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); } Task<string> TaskMethod() { return TaskMethod(TaskScheduler.Default); } Task<string> TaskMethod(TaskScheduler scheduler) { Task delay = Task.Delay(TimeSpan.FromSeconds(5)); return delay.ContinueWith(t => { string str = $"任务运行在{CurrentThread.ManagedThreadId}上. 是否为线程池线程:{CurrentThread.IsThreadPoolThread}"; Console.WriteLine(str); ContentTextBlock.Text = str; return str; }, scheduler); } }
运行结果如下所示,从左至右依次单击按钮,前两个按钮将会引发异常。
具体信息如下所示。
参考书籍
本文主要参考了以下几本书,在此对这些作者表示由衷的感谢,感谢你们为.Net的发扬光大所做的贡献!
- 《CLR via C#》
- 《C# in Depth Third Edition》
- 《Essential C# 6.0》
- 《Multithreading with C# Cookbook Second Edition》
- 《C#多线程编程实战》
源码下载点击链接 示例源码下载
笔者水平有限,如果错误欢迎各位批评指正!
本来想趁待业期间的时间读完《Multithreading with C# Cookbook Second Edition》这本书,并且分享做的相关笔记;但是由于笔者目前职业规划和身体原因,可能最近都没有时间来更新这个系列,没法做到几天一更。请大家多多谅解!但是笔者一定会将这个系列全部更新完成的!感谢大家的支持!
作者:InCerry
出处:https://www.cnblogs.com/InCerry/p/9450493.html
本站使用「署名 4.0 国际」创作共享协议,转载请在文章明显位置注明作者及出处。