Effective C# 学习笔记(三十七) 警惕并行处理中的异常处理
2011-07-26 23:57
573 查看
若一个异常到达了调用该线程的方法时,该线程也就终止运行了。而并行编程使用AggregateException类型来处理发生在子线程中的各类异常,而这些异常存储在AggregateException对象的InnerExceptions属性中。处理异常策略的原则是:处理你能恢复到正常状态的异常,抛出其他那些异常。
下面的代码,是修改上一次笔记中说明Web下载逻辑。使用一个Dictionary结构来处理异常,其键值分别为异常类型(Exception Type)和处理的代理(Action<T>)。
//异常处理段代码
try
{
urls.RunAsync(url
=> startDownload(url),
task
=> finishDownload(task.AsyncState.ToString(),
task.Result));
}
catch
(AggregateException problems)
{
//注册处理各类异常的逻辑
var
handlers = new Dictionary<Type, Action<Exception>>();
handlers.Add(typeof(WebException),ex
=> Console.WriteLine(ex.Message));
if
(!HandleAggregateError(problems, handlers))
throw;//这里抛出了AggregateException,而不是具体的异常,因为InnerExceptions中可能有你需要的其他异常信息。
}
//处理异常的方法
private static bool
HandleAggregateError(AggregateException aggregate,
Dictionary<Type,
Action<Exception>> exceptionHandlers)
{
foreach
(var exception in aggregate.InnerExceptions)
//递归处理所有的内部异常
if
(exception is AggregateException)
return
HandleAggregateError(
exception
as AggregateException, exceptionHandlers);
//若包含处理方法,处理之
else
if (exceptionHandlers.ContainsKey( exception.GetType()))
{
exceptionHandlers[exception.GetType()]
(exception);
}
//否则处理不了,返回false
else
return
false;
return
true;
}
在大多数情况下,处理已知异常,而不是抛出它,不处理它更合适。所以我们修改了开始下载处理逻辑部分的代码:
private static
Task<byte[]> startDownload(string url)
{
var
tcs = new TaskCompletionSource<byte[]>(url);
var
wc = new WebClient();
wc.DownloadDataCompleted
+= (sender, e) =>
{
if
(e.UserState == tcs)
{
if
(e.Cancelled)
tcs.TrySetCanceled();
else
if (e.Error != null)
{
if (e.Error is WebException)//当发生WebException时,将结果设置为0字节
tcs.TrySetResult(new
byte[0]);
else//其他异常情况
tcs.TrySetException(e.Error);
}
else//正常情况
tcs.TrySetResult(e.Result);
}
};
wc.DownloadDataAsync(new
Uri(url), tcs);
return
tcs.Task;
}
上面的代码在处理WebException时,只返回0字节,表示下载的失败,因为该异常明确的说明了服务地址的不可达。
由于Query查询只在有代码访问其结果集的时候会执行,所以不必在定义Query的地方加入try/catch区块,你只需在执行获取Query结果集的部分执行即可。如下代码:
var nums = from n in
data
where
n < 150
select
Factorial(n);
try
{
foreach
(var item in nums)
Console.WriteLine(item);
}
catch
(InvalidOperationException inv)
{
//
elided
}
而在使用PLINQ时,由于其执行顺序的不同,你需要把定义Query的部分也try/catch起来,而你捕获的异常要用AggregateException多线程并发异常类,其内部属性InnerExceptions会返回你要的内部异常,其是由Parallel Task Library提供专门用来处理并发运行中的异常的。若任何一个后台线程抛出异常,整个的后台操作也就停止了。所以你要做的是尽量确保后台不抛出异常,并处理AggregateException异常。
下面的代码,是修改上一次笔记中说明Web下载逻辑。使用一个Dictionary结构来处理异常,其键值分别为异常类型(Exception Type)和处理的代理(Action<T>)。
//异常处理段代码
try
{
urls.RunAsync(url
=> startDownload(url),
task
=> finishDownload(task.AsyncState.ToString(),
task.Result));
}
catch
(AggregateException problems)
{
//注册处理各类异常的逻辑
var
handlers = new Dictionary<Type, Action<Exception>>();
handlers.Add(typeof(WebException),ex
=> Console.WriteLine(ex.Message));
if
(!HandleAggregateError(problems, handlers))
throw;//这里抛出了AggregateException,而不是具体的异常,因为InnerExceptions中可能有你需要的其他异常信息。
}
//处理异常的方法
private static bool
HandleAggregateError(AggregateException aggregate,
Dictionary<Type,
Action<Exception>> exceptionHandlers)
{
foreach
(var exception in aggregate.InnerExceptions)
//递归处理所有的内部异常
if
(exception is AggregateException)
return
HandleAggregateError(
exception
as AggregateException, exceptionHandlers);
//若包含处理方法,处理之
else
if (exceptionHandlers.ContainsKey( exception.GetType()))
{
exceptionHandlers[exception.GetType()]
(exception);
}
//否则处理不了,返回false
else
return
false;
return
true;
}
在大多数情况下,处理已知异常,而不是抛出它,不处理它更合适。所以我们修改了开始下载处理逻辑部分的代码:
private static
Task<byte[]> startDownload(string url)
{
var
tcs = new TaskCompletionSource<byte[]>(url);
var
wc = new WebClient();
wc.DownloadDataCompleted
+= (sender, e) =>
{
if
(e.UserState == tcs)
{
if
(e.Cancelled)
tcs.TrySetCanceled();
else
if (e.Error != null)
{
if (e.Error is WebException)//当发生WebException时,将结果设置为0字节
tcs.TrySetResult(new
byte[0]);
else//其他异常情况
tcs.TrySetException(e.Error);
}
else//正常情况
tcs.TrySetResult(e.Result);
}
};
wc.DownloadDataAsync(new
Uri(url), tcs);
return
tcs.Task;
}
上面的代码在处理WebException时,只返回0字节,表示下载的失败,因为该异常明确的说明了服务地址的不可达。
由于Query查询只在有代码访问其结果集的时候会执行,所以不必在定义Query的地方加入try/catch区块,你只需在执行获取Query结果集的部分执行即可。如下代码:
var nums = from n in
data
where
n < 150
select
Factorial(n);
try
{
foreach
(var item in nums)
Console.WriteLine(item);
}
catch
(InvalidOperationException inv)
{
//
elided
}
而在使用PLINQ时,由于其执行顺序的不同,你需要把定义Query的部分也try/catch起来,而你捕获的异常要用AggregateException多线程并发异常类,其内部属性InnerExceptions会返回你要的内部异常,其是由Parallel Task Library提供专门用来处理并发运行中的异常的。若任何一个后台线程抛出异常,整个的后台操作也就停止了。所以你要做的是尽量确保后台不抛出异常,并处理AggregateException异常。
相关文章推荐
- Effective C# 学习笔记(四十六)对异常进行分类并逐类处理
- Effective C# 学习笔记(四十七)对异常进行strong guarantee 策略处理
- Python3.3 学习笔记5 - 异常处理
- 【python学习笔记】Python异常处理raise、try...except、断言assert
- Effective C# 学习笔记(四十三)使用Expression处理绑定(属性值更改)事件
- JavaSE学习笔记之-----异常处理
- Java学习笔记之异常、处理异常、异常抛出、自定义异常、异常链
- Python 学习笔记 (3)—— python异常处理
- Java学习笔记---其他类特性与异常处理
- 精通SqlServer2005学习笔记----Sqlserver事物中的异常处理
- Guava学习笔记:简化异常处理的Throwables类
- j2me学习笔记【5】——抛出异常处理的小例子
- C++ Primer 学习笔记_88_用于大型程序的工具 --异常处理[续1]
- 中断+异常处理【学习笔记】
- scala 学习笔记(01) 函数定义、分支、循环、异常处理、递归
- Java异常处理学习笔记
- Java学习笔记62. 异常处理语句 try...catch...finally
- 学习笔记之C#类、循环中的语句、基础知识和异常处理
- Python学习笔记ucas(lecture4)异常处理、GUI、SciPy
- spring源码学习笔记-初始化(六)-完成及异常处理