您的位置:首页 > Web前端

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异常。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: