-
c#教程之使用 AggregateException 类处理任务异常
本站最新发布 Python从入门到精通|Python基础教程
试听地址 https://www.xin3721.com/eschool/
在下一个练习中,将展示如何捕捉一个 AggregateException,并用它处理任务取消时抛出的
TaskCanceledException 异常。
Ø 确认取消并处理 AggregateException 异常
1. 在 V isual Studio,显示 GraphWindow.xaml 文件的设计视图。
2. 从工具箱中,将一个 Label 控件拖放到 cancelButton 按钮下方。
Label 控件左边缘要对齐 cancelButton 按钮的左边缘。
3. 使用属性窗口,将 Label 控件的 Name 属性更改为 status,删除 Content 属性中的值。
4. 切换到正在显示 GraphWindow.xaml.cs 文件的“代码和文本编辑器”窗口,在
getDataForGraph 方法后面添加以下方法:
private bool handleException(Exception e)
{
if (e is TaskCanceledException)
{
plotButton.Dispatcher.Invoke(new Action(() =>
{
status.Content = "Tasks Canceled";
}), DispatcherPriority.ApplicationIdle);
return true;
}
else
{
return false;
}
}
这个方法检查作为参数传入的 Exception 对象;如果它是一个 TaskCanceledException 对象, 方法就在 status 标签中显示“Tasks Canceled”(任务已取消),并返回 true 来指明它处理了 异常;否则返回 false。
5. 在 getDataForGraph 方法中,修改创建和运行任务的语句,将 CancellationToken 对象指 定为 StartNew 方法的第二个参数。如以下加粗的代码所示:
private byte[] getDataForGraph(int dataSize)
{
byte[] data = new byte[dataSize];
tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token;
...
first = Task.Factory.StartNew(() => generateGraphData(data, 0, pixelWidth / 8, token), token);
second = Task.Factory.StartNew(() => generateGraphData(data, pixelWidth / 8, pixelWidth / 4, token), token);
third = Task.Factory.StartNew(() => generateGraphData(data, pixelWidth / 4, pixelWidth * 3 / 8, token), token);
fourth = Task.Factory.StartNew(() => generateGraphData(data, pixelWidth * 3 / 8,
pixelWidth / 2, token), token); Task.WaitAll(first, second, third, fourth);
...
}
6. 围绕创建和运行任务的语句添加一个 try 块,并等待它们完成。如果成功等待,就用 Dispatcher.Invoke 方法在 status 标签中显示文本“Tasks Completed”。添加一个 catch 块来处 理 AggregateException 异常。在这个异常处理块中,调用 AggregateException 对象的 Handle 方法,传递对 handleException 方法的一个引用。以下加粗显示的代码是应该进行的更改:
private byte[] getDataForGraph(int dataSize)
{
byte[] data = new byte[dataSize];
tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token;
}
}
...
}
...
}
...
// 任务启动后会运行这里的代码
...
l 如果希望继续学习下一章的学习,请继续运行 V isual Studio 2010,然后阅读第 28 章。
l 如果希望现在就退出 V isual Studio 2010,请选择“文件”|“退出”。如果看到“保存” 对话框,请单击“是”保存项目。
第 27 章快速参考
...
return true;
}
else
{
return false;
}
}
中途取消任务 创建一个 CancellationTokenSource 对象,并在任务运行的方法中使用 一个 CancellationToken 参数,从而实现协作式取消。在任务运行的方法 中,调用 CancellationToken 参数所代表的取消标记对象的 ThrowIfCancellationRequested 方 法 , 从 而 抛 出 一 个 OperationCanceledException 异常并终止任务:
private void generateGraphData(..., CancellationToken token)
{
... token.ThrowIfCancellationRequested();
...
试听地址 https://www.xin3721.com/eschool/
使用 AggregateException 类处理任务异常
贯穿全书,我们一直在强调异常处理是任何商业应用程序中的重要元素。到目前为止,你 遇到的所有异常处理结构使用起来都非常简单。只需决定由什么代码引发异常,并对引发 的异常进行捕捉即可。然而,将工作分解成多个并发的任务之后,异常的跟踪和处理就成 为一个比较复杂的问题。现在的问题是,不同的任务可能分别生成自己的异常,需要以一 种方式捕捉和处理可能同时抛出的多个异常。这正是 AggregateException 类可以大展身手的 时候。
AggregateException 类扮演了一个异常集合的包装器的角色。集合中的每个异常都可能是由 不同的任务抛出的。在你的应用程序中,要捕捉 AggregateException 异常,然后遍历这个集 合,并执行任何必要的处理。为了向你提供帮助,AggregateException 类提供了 Handle 方
法。Handle 方法获取一个 Func<Exception, bool> 委托。该委托引用的方法要获取一个 Exception 对 象 作 为 它 的 参 数 , 并 返 回 一 个 Boolean 值。调用 Handle 时 , 会 为 AggregateException 的集合中的每个异常都运行引用的方法。引用的方法可以检查异常,并 采取恰当的行动。如果引用的方法处理了异常,就应该返回 true。否则,应该返回 false。 Handle 方法完成时,所有未处理的异常都打包到一个新的 AggregateException 对象中,并 抛出这个异常对象;随即,一个外层的异常处理方法可以捕捉这个异常,并对其进行处理。
贯穿全书,我们一直在强调异常处理是任何商业应用程序中的重要元素。到目前为止,你 遇到的所有异常处理结构使用起来都非常简单。只需决定由什么代码引发异常,并对引发 的异常进行捕捉即可。然而,将工作分解成多个并发的任务之后,异常的跟踪和处理就成 为一个比较复杂的问题。现在的问题是,不同的任务可能分别生成自己的异常,需要以一 种方式捕捉和处理可能同时抛出的多个异常。这正是 AggregateException 类可以大展身手的 时候。
AggregateException 类扮演了一个异常集合的包装器的角色。集合中的每个异常都可能是由 不同的任务抛出的。在你的应用程序中,要捕捉 AggregateException 异常,然后遍历这个集 合,并执行任何必要的处理。为了向你提供帮助,AggregateException 类提供了 Handle 方
法。Handle 方法获取一个 Func<Exception, bool> 委托。该委托引用的方法要获取一个 Exception 对 象 作 为 它 的 参 数 , 并 返 回 一 个 Boolean 值。调用 Handle 时 , 会 为 AggregateException 的集合中的每个异常都运行引用的方法。引用的方法可以检查异常,并 采取恰当的行动。如果引用的方法处理了异常,就应该返回 true。否则,应该返回 false。 Handle 方法完成时,所有未处理的异常都打包到一个新的 AggregateException 对象中,并 抛出这个异常对象;随即,一个外层的异常处理方法可以捕捉这个异常,并对其进行处理。
在下一个练习中,将展示如何捕捉一个 AggregateException,并用它处理任务取消时抛出的
TaskCanceledException 异常。
Ø 确认取消并处理 AggregateException 异常
1. 在 V isual Studio,显示 GraphWindow.xaml 文件的设计视图。
2. 从工具箱中,将一个 Label 控件拖放到 cancelButton 按钮下方。
Label 控件左边缘要对齐 cancelButton 按钮的左边缘。
3. 使用属性窗口,将 Label 控件的 Name 属性更改为 status,删除 Content 属性中的值。
4. 切换到正在显示 GraphWindow.xaml.cs 文件的“代码和文本编辑器”窗口,在
getDataForGraph 方法后面添加以下方法:
private bool handleException(Exception e)
{
if (e is TaskCanceledException)
{
plotButton.Dispatcher.Invoke(new Action(() =>
{
status.Content = "Tasks Canceled";
}), DispatcherPriority.ApplicationIdle);
return true;
}
else
{
return false;
}
}
这个方法检查作为参数传入的 Exception 对象;如果它是一个 TaskCanceledException 对象, 方法就在 status 标签中显示“Tasks Canceled”(任务已取消),并返回 true 来指明它处理了 异常;否则返回 false。
5. 在 getDataForGraph 方法中,修改创建和运行任务的语句,将 CancellationToken 对象指 定为 StartNew 方法的第二个参数。如以下加粗的代码所示:
private byte[] getDataForGraph(int dataSize)
{
byte[] data = new byte[dataSize];
tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token;
...
first = Task.Factory.StartNew(() => generateGraphData(data, 0, pixelWidth / 8, token), token);
second = Task.Factory.StartNew(() => generateGraphData(data, pixelWidth / 8, pixelWidth / 4, token), token);
third = Task.Factory.StartNew(() => generateGraphData(data, pixelWidth / 4, pixelWidth * 3 / 8, token), token);
fourth = Task.Factory.StartNew(() => generateGraphData(data, pixelWidth * 3 / 8,
pixelWidth / 2, token), token); Task.WaitAll(first, second, third, fourth);
...
}
6. 围绕创建和运行任务的语句添加一个 try 块,并等待它们完成。如果成功等待,就用 Dispatcher.Invoke 方法在 status 标签中显示文本“Tasks Completed”。添加一个 catch 块来处 理 AggregateException 异常。在这个异常处理块中,调用 AggregateException 对象的 Handle 方法,传递对 handleException 方法的一个引用。以下加粗显示的代码是应该进行的更改:
private byte[] getDataForGraph(int dataSize)
{
byte[] data = new byte[dataSize];
tokenSource = new CancellationTokenSource(); CancellationToken token = tokenSource.Token;
try
{
first = Task.Factory.StartNew(() => generateGraphData(data, 0, pixelWidth / 8, token), token);
second = Task.Factory.StartNew(() => generateGraphData(data, pixelWidth / 8, pixelWidth / 4, token), token);
third = Task.Factory.StartNew(() => generateGraphData(data, pixelWidth / 4,
pixelWidth * 3 / 8, token), token);
fourth = Task.Factory.StartNew(() => generateGraphData(data, pixelWidth * 3 / 8, pixelWidth / 2, token), token);
Task.WaitAll(first, second, third, fourth);
plotButton.Dispatcher.Invoke(new Action(() =>
{
{
first = Task.Factory.StartNew(() => generateGraphData(data, 0, pixelWidth / 8, token), token);
second = Task.Factory.StartNew(() => generateGraphData(data, pixelWidth / 8, pixelWidth / 4, token), token);
third = Task.Factory.StartNew(() => generateGraphData(data, pixelWidth / 4,
pixelWidth * 3 / 8, token), token);
fourth = Task.Factory.StartNew(() => generateGraphData(data, pixelWidth * 3 / 8, pixelWidth / 2, token), token);
Task.WaitAll(first, second, third, fourth);
plotButton.Dispatcher.Invoke(new Action(() =>
{
status.Content = "Tasks Completed";
}), DispatcherPriority.ApplicationIdle);
}
catch (AggregateException ae)
{
ae.Handle(handleException);
}
String message = String.Format("Status of tasks is {0}, {1}, {2}, {3}", first.Status, second.Status, third.Status, fourth.Status);
MessageBox.Show(message);
return data;
}
7. 在 generateDataForGraph 方法中,将检查 CancellationToken 对象的 IsCancellationProperty
的 if 语句替换成调用 ThrowIfCancellationRequested 方法的代码,如以下加粗的代码所示:
private void generateDataForGraph( byte[] data, int partitionStart, int partitionEnd, CancellationToken token)
{
...
for (int x = partitionStart; x < partitionEnd; x++);
{
...
for (double i = -p; I < p; i += 3)
{
token.ThrowIfCancellationRequested();
}), DispatcherPriority.ApplicationIdle);
}
catch (AggregateException ae)
{
ae.Handle(handleException);
}
String message = String.Format("Status of tasks is {0}, {1}, {2}, {3}", first.Status, second.Status, third.Status, fourth.Status);
MessageBox.Show(message);
return data;
}
7. 在 generateDataForGraph 方法中,将检查 CancellationToken 对象的 IsCancellationProperty
的 if 语句替换成调用 ThrowIfCancellationRequested 方法的代码,如以下加粗的代码所示:
private void generateDataForGraph( byte[] data, int partitionStart, int partitionEnd, CancellationToken token)
{
...
for (int x = partitionStart; x < partitionEnd; x++);
{
...
for (double i = -p; I < p; i += 3)
{
token.ThrowIfCancellationRequested();
}
}
...
}
...
8. 在“调试”菜单中选择“开始执行(不调试)”来生成并运行应用程序。
9. 在 Graph Demo 窗口中,单击 Plot Graph 按钮,验证每个任务的状态都 是
RanToCompletion,图表顺利生成,而且 status 标签显示消息“Tasks Completed”。
10. 再次单击 Plot Graph 按钮,这一次快速单击 Cancel。如果动作足够快,会看到一个或 多个任务的状态报告为 Canceled,status 标签应显示文本“Tasks Canceled”,而且图表中应 出现空洞。如果动作不够快,请重复这个步骤,再试一遍。
11. 关闭 Graph Demo 窗口,返回 V isual Studio。
27.4.3 为 Canceled 和 Faulted 任务使用延续任务
如果需要在一个任务被取消或者引发未处理异常的时候执行额外的工作,记住可以利用 ContinueWith 方法,并传递恰当的 TaskContinuationOptions 值。例如,以下代码创建一个 任务来运行 doWork 方法。如果任务被取消,ContinueWith 方法指定创建另一个任务来运 行 doCancellationWork 方法。这个方法可执行一些简单的日志记录或者清理工作。如果任 务没有取消,延续任务不会运行。
Task task = new Task(doWork);
task.ContinueWith(doCancellationWork, TaskContinuationOptions.OnlyOnCanceled);
task.Start();
...
private void doWork()
{
9. 在 Graph Demo 窗口中,单击 Plot Graph 按钮,验证每个任务的状态都 是
RanToCompletion,图表顺利生成,而且 status 标签显示消息“Tasks Completed”。
10. 再次单击 Plot Graph 按钮,这一次快速单击 Cancel。如果动作足够快,会看到一个或 多个任务的状态报告为 Canceled,status 标签应显示文本“Tasks Canceled”,而且图表中应 出现空洞。如果动作不够快,请重复这个步骤,再试一遍。
11. 关闭 Graph Demo 窗口,返回 V isual Studio。
27.4.3 为 Canceled 和 Faulted 任务使用延续任务
如果需要在一个任务被取消或者引发未处理异常的时候执行额外的工作,记住可以利用 ContinueWith 方法,并传递恰当的 TaskContinuationOptions 值。例如,以下代码创建一个 任务来运行 doWork 方法。如果任务被取消,ContinueWith 方法指定创建另一个任务来运 行 doCancellationWork 方法。这个方法可执行一些简单的日志记录或者清理工作。如果任 务没有取消,延续任务不会运行。
Task task = new Task(doWork);
task.ContinueWith(doCancellationWork, TaskContinuationOptions.OnlyOnCanceled);
task.Start();
...
private void doWork()
{
}
...
// 任务启动后会运行这里的代码
...
private void doCancellationWork(Task task)
{
// 任务会在dowWork 取消时运行这里的代码
...
}
类似地,可用 TaskContinuationOptions.OnlyOnFaulted 指定一个延续任务只有在任务运行的 原始方法引发一个未处理的异常时才运行。
本章讲述了为什么有必要写程序将工作分散到多个处理器和处理器内核上。讲述了如何使 用“任务并行库”(TPL)来并行执行操作,以及如何同步并发操作,并等待它们完成。讲 述了如何用 Parallel 类对一些常见的编程构造进行“并行化”,还讲述了在什么时候不应该 对代码进行并行化。最后,本章讲述了如何在图形用户界面中配合使用任务和线程来增强 响应能力和吞吐能力,还讲述了如何以一种得体的、受控制的方式取消任务。
{
// 任务会在dowWork 取消时运行这里的代码
...
}
类似地,可用 TaskContinuationOptions.OnlyOnFaulted 指定一个延续任务只有在任务运行的 原始方法引发一个未处理的异常时才运行。
本章讲述了为什么有必要写程序将工作分散到多个处理器和处理器内核上。讲述了如何使 用“任务并行库”(TPL)来并行执行操作,以及如何同步并发操作,并等待它们完成。讲 述了如何用 Parallel 类对一些常见的编程构造进行“并行化”,还讲述了在什么时候不应该 对代码进行并行化。最后,本章讲述了如何在图形用户界面中配合使用任务和线程来增强 响应能力和吞吐能力,还讲述了如何以一种得体的、受控制的方式取消任务。
l 如果希望继续学习下一章的学习,请继续运行 V isual Studio 2010,然后阅读第 28 章。
l 如果希望现在就退出 V isual Studio 2010,请选择“文件”|“退出”。如果看到“保存” 对话框,请单击“是”保存项目。
第 27 章快速参考
目 标 | 操 作 | |
创建任务并运行它 |
要么使用 TaskFactory 对象的 StartNew 方法来,在一个步骤中创建并运 行任务: Task task = taskFactory.StartNew(doWork()); ... private void doWork() { // 任务启动时会运行这里的代码 ... } 要么新建一个 Task 对象,让它引用准备运行的方法,再调用 Start 方法: Task task = new Task(doWork); task.Start(); |
|
等待一个任务完成 |
调用 Task 对象的 Wait 方法: Task task = ...; ... task.Wait(); |
|
等待几个任务完成 |
调用 Task 类的静态 WaitAll 方法,并指定要等待的多个任务: Task task1 = ...; Task task2 = ...; Task task3 = ...; Task task4 = ...; ... Task.WaitAll(task1, task2, task3, task4); |
|
指定当一个任务完成 后,在一个新任务中运 行另一个方法 |
这就是所谓的“延续”。调用任务的 ContinueWith 方法,将要运行的方法 指定为一个“延续”: Task task = new Task(doWork); task.ContinueWith(doMoreWork, TaskContinuationOptions.NotOnFaulted); |
从任务返回值 |
用一个 Task<TResult>对象运行一个方法,其中类型参数 TResult 指定了 方法的返回值的类型。使用任务的 Result 属性等待任务完成并返回值: Task<int> calculateValueTask = new Task<int>(() => calculateValue(...)); calculateValueTask.Start(); // 调用 calculateValue 方法 ... int calculatedData = calculateValueTask.Result; // 阻塞, 直到 calculateValueTask 完成 |
使用并行的任务来执 行循环迭代和语句序 列 |
使用 Parallel.For 和 Parallel.ForEach 方法,用任务来执行循环迭代: Parallel.For(0, 100, performLoopProcessing); ... private void performLoopProcessing(int x) { // 执行循环处理 } 使用 Parallel.Invoke 方法,用单独的任务并发执行多个方法: Parallel.Invoke( doWork, doMoreWork, doYetMoreWork ); |
处理一个或多个任务 抛出的异常 |
捕捉 AggregateException 异 常 。 使 用 Handle 方 法 指 定 可 对 AggregateException 对象中的每个异常进行处理的一个方法。在这个方 法中,如果异常得到处理,就返回 true;否则返回 false: try { Task task = Task.Factory.StartNew(...); ... } catch (AggregateException ae) { ae.Handle(new Func<Exception, bool> (handleException)); } ... private bool handleException(Exception e) { if (e is TaskCanceledException) { |
...
return true;
}
else
{
return false;
}
}
中途取消任务 创建一个 CancellationTokenSource 对象,并在任务运行的方法中使用 一个 CancellationToken 参数,从而实现协作式取消。在任务运行的方法 中,调用 CancellationToken 参数所代表的取消标记对象的 ThrowIfCancellationRequested 方 法 , 从 而 抛 出 一 个 OperationCanceledException 异常并终止任务:
private void generateGraphData(..., CancellationToken token)
{
... token.ThrowIfCancellationRequested();
...
最新更新
python中装饰器修复技术
python中filter、map、reduce的区别
python爬虫--cookie反爬处理
Python 模拟 Base64编码
证明task线程是来源于线程池的,线程重用
Python基础总结之第一天(新手可相互督促
Django:web认识,jinja2模块,如何安装Djang
tcp服务端无线为多个客户端服务
python自动化测试之DDT数据驱动
python输出九九乘法表
基于UDP的服务器端和客户端
再谈UDP和TCP
在socket编程中使用域名
网络数据传输时的大小端问题
socket编程实现文件传输功能
如何优雅地断开TCP连接?
图解TCP四次握手断开连接
详细分析TCP数据的传输过程
图解TCP数据报结构以及三次握手(非常详
TCP协议的粘包问题(数据的无边界性)
存储过程用法
MySQL教程之SQL ORDER BY 子句
MySQL教程之SQL AND & OR 运算符
MySQL教程之SQL WHERE 子句
MySQL教程之SQL SELECT DISTINCT 语句
MySQL教程之SQL SELECT 语句
MySQL教程之SQL语法
MySQL教程之SQL 简介
MySQL教程之MySQL之pymysql
MySQL教程之mysql设置不区分大小写