乐趣区

关于.net:如何使用-C-在异步代码中处理异常

异样解决 是一种解决运行时谬误的技术,而 异步编程 容许咱们在解决资源密集型的业务逻辑时不须要在 Main 办法或者在 执行线程 中被阻塞,值得注意的是,异步办法和同步办法的异样解决机制是不一样的,本篇咱们就来探讨下如何在异步办法中解决异样。

## 异步办法 VS 同步办法 的异样解决

在同步代码中抛出异样,它会始终以冒泡的形式往上抛,直到遇到能够解决这个异样的 catch 块为止,能够设想,异步办法中的异样抛出必定要比这个简单。

大家都晓得 异步办法 能够有三种返回类型,如:void, Task, Task<TResult>,当异样办法的返回值是 Task,Task<TResult> 的办法中抛出异样的话,这个异样对象会被塞到 AggregateException 对象中,而后包裹在 Task 中进行返回,有些敌人可能要问,如果异步办法中抛出了几个异样怎么办?其实也是一样的情理,这些异样对象都会被塞到 AggregateException 中通过 Task 去返回。

最初,如果异样呈现在返回值为 void 的异步办法中,异样是在调用这个异步办法的 SynchronizationContext 同步上下文上触发。

返回 void 异步办法中的异样

上面的程序展现了返回 void 的异步办法中抛出了异样。


    class Program
    {static void Main(string[] args)
        {ThisIsATestMethod();

            Console.ReadLine();}

        public static void ThisIsATestMethod()
        {
            try
            {AsyncMethodReturningVoid();
            }
            catch (Exception ex)
            {Console.WriteLine(ex.Message);
            }
        }
        private static async void AsyncMethodReturningVoid()
        {await Task.Delay(1000);
            throw new Exception("This is an error message...");
        }
    }

从图中能够看到,AsyncMethodReturningVoid 办法抛出的异样会被包裹此办法的 try catch 捕捉到。

返回 Task 的异步办法异样

当异样从返回值为 Task 的异步办法中抛出,这个异样对象会被包裹在 Task 中并且返回给办法调用方,当你用 await 期待此办法时,只会失去一组异样中的第一个被触发的异样,如果有点懵的话,如下代码所示:


    class Program
    {static void Main(string[] args)
        {ExceptionInAsyncCodeDemo();

            Console.ReadLine();}

        public static async Task ExceptionInAsyncCodeDemo()
        {
            try
            {var task1 = Task.Run(() => throw new IndexOutOfRangeException("IndexOutOfRangeException is thrown."));
                var task2 = Task.Run(() => throw new ArithmeticException("ArithmeticException is thrown."));
                await Task.WhenAll(task1, task2);
            }
            catch (AggregateException ex)
            {Console.WriteLine(ex.Message);
            }
            catch (Exception ex)
            {Console.WriteLine(ex.Message);
            }
        }
    }

从下面代码中能够看出 task1 和 task2 都会抛出异样,但在 catch 块中只捕捉了 task1 中的异样,这就阐明返回值为 Task 的多个异样的办法中,调用方只能截获第一次产生异样的异样对象。

应用 Exceptions 属性 获取所有异样

要想获取已抛出的所有异样,能够利用 Task.Exceptions 属性来获取,上面的代码清单展现了如何在返回 Task 的办法中获取所有的异样信息。


    class Program
    {static void Main(string[] args)
        {ExceptionInAsyncCodeDemo();

            Console.ReadLine();}

        public static async Task ExceptionInAsyncCodeDemo()
        {
            Task tasks = null;
            try
            {var task1 = Task.Run(() => throw new IndexOutOfRangeException("IndexOutOfRangeException is thrown."));
                var task2 = Task.Run(() => throw new ArithmeticException("ArithmeticException is thrown."));
                tasks = Task.WhenAll(task1, task2);
                await tasks;
            }
            catch
            {
                AggregateException aggregateException = tasks.Exception;

                foreach (var e in aggregateException.InnerExceptions)
                {Console.WriteLine(e.GetType().ToString());
                }
            }
        }
    }

应用 AggregateException.Handle 解决所有异样

你能够利用 AggregateException.Handle 属性去解决一组异样中的某一个,同时疏忽其余你不关怀的异样,上面的代码片段展现了如何去实现。


    class Program
    {static async Task Main(string[] args)
        {await ExceptionInAsyncCodeDemo();
            Console.Read();}

        public static async Task ExceptionInAsyncCodeDemo()
        {
            Task tasks = null;
            try
            {var task1 = Task.Run(() => throw new IndexOutOfRangeException("IndexOutOfRangeException is thrown."));
                var task2 = Task.Run(() => throw new ArithmeticException("ArithmeticException is thrown."));
                tasks = Task.WhenAll(task1, task2);
                await tasks;
            }
            catch(AggregateException ex)
            {
                AggregateException aggregateException = tasks.Exception;

                foreach (var e in aggregateException.InnerExceptions)
                {Console.WriteLine(e.GetType().ToString());
                }
            }
        }
    }

下面的代码片段示意:IndexOutOfRangeException 会被解决, InvalidOperationException 会被疏忽。

最初想说的是,你能够利用 异步编程 来进步程序的扩展性和吞吐率,当你在应用异步办法时,请留神在异步办法中的异样解决语义和同步办法中的异样解决是不一样的。

译文链接:https://www.infoworld.com/art…

退出移动版