zoukankan      html  css  js  c++  java
  • 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开始计时,好吧,很好理解,我却疑惑了半天)

  • 相关阅读:
    LeetCode 24. Swap Nodes in Pairs (两两交换链表中的节点)
    LeetCode 1041. Robot Bounded In Circle (困于环中的机器人)
    LeetCode 1037. Valid Boomerang (有效的回旋镖)
    LeetCode 1108. Defanging an IP Address (IP 地址无效化)
    LeetCode 704. Binary Search (二分查找)
    LeetCode 744. Find Smallest Letter Greater Than Target (寻找比目标字母大的最小字母)
    LeetCode 852. Peak Index in a Mountain Array (山脉数组的峰顶索引)
    LeetCode 817. Linked List Components (链表组件)
    LeetCode 1019. Next Greater Node In Linked List (链表中的下一个更大节点)
    29. Divide Two Integers
  • 原文地址:https://www.cnblogs.com/grj001/p/12225405.html
Copyright © 2011-2022 走看看