• C#多线程中的异常处理


    C#多线程中的异常处理

    常规Thread中处理异常

    使用Thread创建的子线程,需要在委托中捕捉,无法在上下文线程中捕捉

    复制代码

    static void Main(string[] args)
    {
        ThreadStart threadStart = DoWork;
        Thread thread = new Thread(threadStart);
        thread.Start();
        thread.Join();
    }
    static void DoWork()
    {
        try
        {
            throw new Exception("子线程出现异常了");
        }
        catch (Exception ex)
        {
            Trace.Assert(false, "Catch In Delegate");
        }
    }

    复制代码

    Task中处理异常

    1.仍然可以在委托中捕获异常

    2.可以捕获Task.Wait() 或者 Task.Result 的 AggregateException 异常

    复制代码

    try
    {
        task.Wait();
    }
    catch (AggregateException ex)
    {
        Console.WriteLine($"Error: {ex.GetType().Name}");
        foreach (Exception item in ex.InnerExceptions)
        {
            Console.WriteLine($"{item.GetType().Name}, {item.Message}");
        }
    }

    复制代码

     AggregateException 是并行任务中捕获的一组异常

    通过延续任务捕获前驱任务中的异常

    复制代码

    static void Main(string[] args)
    {
        Task task = Task.Run(() => throw new Exception("前驱任务异常了"));
        Task faultedTask = task.ContinueWith(antecedentTask =>
        {
            antecedentTask.Exception.Handle(eachE =>
            {
                Console.WriteLine($"Error: {eachE.Message}");
                return true;
            });
        },TaskContinuationOptions.OnlyOnFaulted);
        faultedTask.Wait();
    }

    复制代码

    前驱任务:使用Run书写的第一个任务就是前驱任务

    延续任务:在一个任务后使用ContinueWith添加的任务就是延续任务,延续一般是一个全新的工作线程

    TaskContinuationOptions:指定延续任务时的可配置项,默认情况下前驱任务完成后,立即执行延续任务,OnlyOnFaulted表示只有前驱任务失败(出异常的时候)才会执行这一个延续任务

    Task.Exception也是一个AggregateException 异常

    注意:

    1.当指定的TaskContinuationOptions与前驱任务运行结果不一致时,强制调用延续任务Wait()会引发TaskCanceledException异常

    复制代码

    static void Main(string[] args)
    {
        Task task = new Task(() =>
        {
            Console.WriteLine("前驱动任务执行中...");
        });
        Task faultedTask = task.ContinueWith(antecedentTask =>
        {
            Console.WriteLine("延续动任务执行中...");
        }, TaskContinuationOptions.OnlyOnFaulted);
        task.Start();
        try
        {
            faultedTask.Wait();
        }
        catch (AggregateException ex)
        {
            Console.WriteLine($"Error: {ex.GetType().Name}");
            foreach (Exception item in ex.InnerExceptions)
            {
                Console.WriteLine($"{item.GetType().Name}, {item.Message}");
            }
        }
        Console.WriteLine($"前驱任务状态{task.Status}");
        Console.WriteLine($"延续任务状态{faultedTask.Status}");
    }

    复制代码

    Ctrl+F5 输出

    补充:

    假如在前驱任务中出现了异常,如OnlyOnFaulted所愿,会执行faultedTask任务,并且在faultedTask.Wait()中不会捕捉到前驱任务的异常,具体看下面一点

    2.延续任务虽然在异步任务中提供了类似if else 的ContinueWith但是在异常处理上还是有点局限,看一个例子

    复制代码

    static void Main(string[] args)
    {
        Task task = Task.Run(()
            =>
        throw new Exception("前驱任务异常了"));
        Task task1 = task.ContinueWith(antecedentTask =>
        {
            throw new Exception("延续任务1异常了");
        });
        Task task2 = task1.ContinueWith(antecedentTask =>
        {
            throw new Exception("延续任务2异常了");
        });
        Task task3 = task2.ContinueWith(antecedentTask =>
        {
            throw new Exception("延续任务3异常了");
        });
        try
        {
            task3.Wait();
        }
        catch (AggregateException ex)
        {
            Console.WriteLine($"Error: {ex.GetType().Name}");
            foreach (Exception item in ex.InnerExceptions)
            {
                Console.WriteLine($"{item.GetType().Name}, {item.Message}");
            }
        }
    }

    复制代码

    Ctrl+F5 输出

    其实这样也可以理解,task3.Wait()只会收集task3所在工作线程上的异常,遗憾的是Task.Exception.InnerExceptions是一个只读集合,这样一来,每个任务的异常只能在各自委托中处理了,事实上也应该如此,可以使用TaskContinuationOptions进行灵活控制

    使用CancellationTokenSource取消任务

    复制代码

    static void Main(string[] args)
    {
        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
        cancellationTokenSource.Token.Register(() => 
        {
            Console.WriteLine("任务取消了");
        });
        cancellationTokenSource.CancelAfter(2000);
        Task task = Task.Run(() =>
        {
            while (true && !cancellationTokenSource.IsCancellationRequested)
            {
                Console.WriteLine("任务执行中...");
                Thread.Sleep(300); 
            }
        },
        cancellationTokenSource.Token);
        task.Wait();
        Console.WriteLine($"任务的最终状态是:{task.Status}");
    }

    复制代码

    Ctrl+F5 输出

    正常取消的任务最终状态是 RanToCompletion ,这里要注意的是,CancelAfter()是在这个方法调用的那一刻开始计时的(并非以Run开始计时,好吧,很好理解,我却疑惑了半天)

  • 相关阅读:
    设计模式-简单工厂模式、工厂模式、抽象工厂模式-(创建型模式)
    设计模式-类间关系
    设计模式-六大基本原则
    设计模式-总起
    [转载]常见的移动端H5页面开发遇到的坑和解决办法
    [转载]Angular4 组件通讯方法大全
    【angular5项目积累总结】文件下载
    【转载】Vue 2.x 实战之后台管理系统开发(二)
    [转载]Vue 2.x 实战之后台管理系统开发(一)
    WordPress翻译中 __()、_e()、_x、_ex 和 _n 的用法及区别
  • 原文地址:https://www.cnblogs.com/grj001/p/12225405.html
Copyright © 2020-2023  润新知