• C#多线程异常处理(三)


    一、异常的抛出与进程终止
    为了简化开发者基于task进行异步编程的难度, .NET Framework4.5改变了未监测异常的默认行为,尽管未监测异常依然会触发UnobservedTaskException异常,但进程默认情况下不再会终止。取而代之的是,异常触发后,运行时会自动处理,不管事件处理器是否检测到了异常,都会将其忽略而不造成进程终止。从.NET Framework 4.5开始,.NET Framework 4中的进程终止机制都可以通过配置被捕捉。

    <configuration>
    <runtime>
    <ThrowUnobservedTaskExceptions enabled="true"/>
    </runtime>
    </configuration>

    二、AggregateException
    AggregateException类的作用是将多个异常集中到一个可抛出的异常中。
    它有个InnerExceptions属性,是一个只读集合类,可通过遍历该集合查找集中的所有单个异常。

    catch (AggregateException ag)
    {
    foreach (var item in ag.InnerExceptions)
    {
    Console.WriteLine("*********集合异常***********");
    Console.WriteLine(item.ToString());
    Console.WriteLine("*********集合异常***********");
    }
    }

    其有两个重要的方法Handle()和Flatten()
    Handle为AggregateException中包含的每个异常都调用一个回调方法。回调方法可以为每个异常决定如何处理;回调返回true表示异常已处理;false表示未处理。调用Handle后,如果至少有一个异常没有处理,就创建一个新的AggregateException对象,其中只包含未处理的异常,并抛出这个新的AggregateException对象。

    catch (AggregateException ae) {
    ae.Handle((x) =>
    {
    if (x is UnauthorizedAccessException) // This we know how to handle.
    {
    Console.WriteLine("You do not have permission to access all folders in this path.");
    Console.WriteLine("See your network administrator or try another path.");
    return true;
    }
    return false; // Let anything else stop the application.
    });
    }

    AggregateException是聚合异常的,而Flatten方法的作用是将异常展开,并重新抛出,有助于客户端的处理。

    catch (AggregateException ag)
    {
    throw ag.Flatten();


    三、TaskScheduler.UnobservedTaskException事件

    class Program
    {
    static void Main(string[] args)
    {
    //AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
    try
    {
    //端口错误,连接数据库必然报错
    string conn = "host=localhost;user = root; password = sheng; port = 3600; database = groupcustomerhotline_log;Character Set=utf8;pooling=true;Connection Timeout=200;Connection Lifetime=200;";
    string sql = "select * from `tnet_activealarm` limit 3";
    StringBuilder sb = new StringBuilder();
    // int a = Convert.ToInt32(sql);//此异常不由异步操作触发,触发类型不为AggregateException。只有由task处罚的异常才会由AggregateException捕捉
    TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs eventArgs) =>
    {
    //eventArgs.SetObserved();
    foreach (var item in eventArgs.Exception.InnerExceptions)
    {
    Console.WriteLine("*********未监测异常***********");
    Console.WriteLine(item.ToString());
    Console.WriteLine("*********未监测异常***********");
    }
    };

    //调用Continue不会等同于调用Result和Wait,触发的是未处理异常而不是集合异常,但是continuewith在不捕捉时会导致进程终止
    MySqlHelper.ExecuteDatasetAsync(conn, sql)
    .ContinueWith((task) =>
    {
    Console.WriteLine("Read Finished!");
    Console.WriteLine(task.Result.Tables[0].Rows.Count);//此处能被捕捉,且不终止进程,这个地方为啥是未处理异常
    foreach (DataRow row in task.Result.Tables[0].Rows)
    {
    sb = new StringBuilder();
    foreach (DataColumn head in task.Result.Tables[0].Columns)//head
    {
    sb.Append(row[head]).ToString();
    }
    Console.WriteLine(sb.ToString());
    }
    });


    var sqltask = MySqlHelper.ExecuteDatasetAsync(conn, sql);//这里的task中有错误,即使不被调用,也会被未处理异常捕捉

    Action<string> ac = task => { Console.WriteLine(task); };//此处只是一个action,不涉及task
    ac("hello");

    Task.Run(() => { throw new NullReferenceException(); }).Wait();//此处能被捕捉,且不终止进程,因为没调用result或者wait,所以是未捕捉异常

    sqltask.Wait();//能被捕捉,且终止进程
    Action<Task<DataSet>> action = task => { Console.WriteLine(task.Result.Tables[0].Rows.Count); };//此处只是一个action,不涉及task
    Task t = new Task((task) => { Console.WriteLine(((Task<DataSet>)task).Result.Tables[0].Rows.Count); }, sqltask);//能被捕捉,且不终止进程 //t.Start();
    }
    catch (AggregateException ag)
    {
    foreach (var item in ag.InnerExceptions)
    {
    Console.WriteLine("*********集合异常***********");
    Console.WriteLine(item.ToString());
    Console.WriteLine("*********集合异常***********");
    }
    }
    catch (Exception ex)
    {
    Console.WriteLine("*********异常***********");
    Console.WriteLine(ex.ToString());
    Console.WriteLine("*********异常***********");
    }


    Thread.Sleep(100);//给异常抛出留有时间,避免此处执行完成后才抛出异常
    GC.Collect();
    GC.WaitForPendingFinalizers();

    Console.WriteLine("Aysnc Finished!");
    Console.ReadLine();
    }

    private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
    Console.WriteLine("未捕捉异常!!!!!!");
    string path = AppDomain.CurrentDomain.BaseDirectory + Path.DirectorySeparatorChar + "Log";
    Directory.CreateDirectory(path);
    using (StreamWriter sw = new StreamWriter(path+Path.DirectorySeparatorChar+"log.txt",true))
    {
    //sw.WriteAsync(e.ToString()).ContinueWith(()=> { sw.Flush(); });
    sw.Write(e.ExceptionObject.ToString());
    sw.Flush();
    }
    //Console.WriteLine(e.ExceptionObject.ToString());
    Console.WriteLine("未捕捉异常!!!!!!");
    }

    private static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
    {
    //throw new NotImplementedException();
    Console.WriteLine("Fine unobserved exception..." + sender.ToString());
    foreach (var iten in e.Exception.InnerExceptions)
    {
    Console.WriteLine(iten.ToString());
    }
    }
    }

    有时候该事件不会被触发,主要是事件处理器缺少以下语句
    Thread.Sleep(100);//给异常抛出的时间
    GC.Collect();//垃圾回收触发未检测异常的处理
    GC.WaitForPendingFinalizers();

    四、调用await后的Task异常处理

    前面说过,Task对象通常抛出一个AggregateException,可查询该异常的InnerExceptions属性来查看真正发生了什么异常。但将await用于Task时,抛出的是第一个内部异常而不是AggregateException。这个设计提供了自然的编程体验。否则就必须在代码中捕捉AggregateException检查异步内部异常,然后要么处理异常要么重新抛出,这会过于繁琐。

  • 相关阅读:
    通过Flex读写浏览器中的cookie的方法整理
    GXCMS模板说明文档
    iBatis简单入门教程
    深入浅出 JavaScript 中的 this
    第二章(2) CSS
    DWR入门教程
    mysql数据库导入sql文件Mysql导入导出.sql文件的方法
    document.getElementById与getElementByName的区别
    Understanding JavaScript Function Invocation and “this”
    if ( document.all ) 可以简单的判断浏览器是否IE浏览器?
  • 原文地址:https://www.cnblogs.com/xietianjiao/p/15188877.html
Copyright © 2020-2023  润新知