• Effective C# 学习笔记(三十七) 警惕并行处理中的异常处理


    若一个异常到达了调用该线程的方法时,该线程也就终止运行了。而并行编程使用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异常。

  • 相关阅读:
    【爬坑】在 IDEA 中运行 Hadoop 程序 报 winutils.exe 不存在错误解决方案
    【爬坑】Vim 文档加密 & 解密
    Maven 安装配置
    2014/11/23 条件查询
    2014/11/21
    2014/11/20 SQL简单命令
    2014/11/19 SQL Server基础
    7、数组
    6、循环、跳转、异常语句,string类、math、datetime
    5、循环语句、穷举
  • 原文地址:https://www.cnblogs.com/haokaibo/p/2117783.html
Copyright © 2020-2023  润新知