并发编程并发编程并发编程 --- 异步方法的异常处理
Fantasy-ke引言
现在模拟一个异步方法抛出了异常:
1 2 3 4 5
| public static async Task ThrowAfter(int ms, string message) { await Task.Delay(ms); throw new Exception(message); }
|
思考一下, DontHandle() 方法是否能够捕获到异常?
1 2 3 4 5 6 7 8 9 10 11 12
| public static void DontHandle() { try { ThrowAfter(1000, "first"); } catch (Exception ex) { Console.WriteLine(ex.Message); }
}
|
答案是:不会捕获到异常!
因为 DontHandle() 方法在 ThrowAfter() 方法抛出异常之前,就已经执行完毕。
异步方法的异常处理
那么上述代码怎么才能捕获到异常呢?
若想要捕获异常则必须通过 await 关键字等待 ThrowAfter() 方法执行完成。
将上文中的代码段进行修改:
1 2 3 4 5 6 7 8 9 10 11
| public static async void HandleoOnError() { try { await ThrowAfter(1000, "first"); } catch (Exception ex) { Console.WriteLine(ex.Message); } }
|
结果就会输出:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public static async void StartTwoTasks() { try { await ThrowAfter(1000, "first"); await ThrowAfter(1000, "second"); Console.WriteLine("StartTwoTasks is Complate"); } catch (Exception ex) { Console.WriteLine(ex.Message); } }
|
思考一下输出是什么?
答案是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public static async void StartTwoTasksParallel() { try { Task t1 = ThrowAfter(1000, "first"); Console.WriteLine("t1 is Complate"); Task t2 = ThrowAfter(1000, "second"); Console.WriteLine("t2 is Complate"); await Task.WhenAll(t2, t1); } catch (Exception ex) { Console.WriteLine(ex.Message); } }
|
输出:
1 2 3
| is Complate t2 is Complate second
|
从输出可以看出来,使用 WhenAll() 方法,两个任务都是执行完成的,但是,捕获异常只能捕获 WhenAll()方法参数中,排在最前面的,且第一个抛出异常的任务的消息,
上述方式有缺陷,只能抛出一个异常的任务的消息,可以将上面的方式再进化一下,如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public static async void StartTwoTasksParallelEx() { Task t1 = null; Task t2 = null; try { t1 = ThrowAfter(1000, "first"); t2 = ThrowAfter(1000, "second"); await Task.WhenAll(t2, t1);
} catch (Exception ex) { if (t1.IsFaulted) { Console.WriteLine(t1.Exception.InnerException.Message); }
if (t2.IsFaulted) { Console.WriteLine(t2.Exception.InnerException.Message); } } }
|
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public static async void StartTwoTasksParallelEx2() { Task t3 = null; try { Task t1 = ThrowAfter(1000, "first"); Task t2 = ThrowAfter(1000, "second"); await (t3 = Task.WhenAll(t2, t1));
} catch (Exception ex) { foreach (var item in t3.Exception.InnerExceptions) { Console.WriteLine("InnerException:" + item.Message); } } }
|
输出: