注意:返回void的异常方法不会等待。这是因为从async void方法抛出的异常无法捕获,因此,异步方法最好返回一个Task类型。处理程序方法或者重写基类方法不受此规则限制。
异步方法的异常较好的一个处理方式,就是使用await关键字,将其放在try/catch语句中。如下:
public static async Task ThrowExcrptionAsync(int ms, string message) { await Task.Delay(ms); throw new Exception(message); } public static async Task Main(string[] args) { //以下语句不会捕捉到异常(方法已经执行完毕,而throw new Exception(message)这句话还没执行,所以上面这段代码并不会捕获到异常) try { ThrowExcrptionAsync(2000, "first"); } catch (Exception e) { Console.WriteLine(e.Message); } Console.ReadKey();
try { await ThrowExcrptionAsync(2000, "first"); } //使用await关键字捕捉异常
catch (Exception e)
{
Console.WriteLine(e.Message); } Console.ReadKey();
} }
在任务中,处理异常和其它异步方式处理异常类似,如果能在所发生异常的线程中处理,那么不要在其它地方处理。但是对于一些不可预料的异常,那么可以通过几种方式来处理。
可以通过访问task.Result
属性来处理异常,因为访问这个属性的Get
方法会使当前线程等待直到该任务完成,并将异常传播给当前线程,这样就可以通过try catch
语句块来捕获异常。另外使用task.GetAwaiter().GetResult()
方法和第使用task.Result
类似,同样可以捕获异常。如果是要捕获多个任务中的异常错误,那么可以通过ContinueWith()
方法来处理。
具体如何实现,演示代码如下所示。
static void Main(string[] args) { Task<int> task; // 在主线程中调用 task.Result task中的异常信息会直接抛出到 主线程中 try { task = Task.Run(() => TaskMethod("Task 1", 2)); int result = task.Result; WriteLine($"结果为: {result}"); } catch (Exception ex) { WriteLine($"异常被捕捉:{ex.Message}"); } WriteLine("------------------------------------------------"); WriteLine(); // 同上 只是访问Result的方式不同 try { task = Task.Run(() => TaskMethod("Task 2", 2)); int result = task.GetAwaiter().GetResult(); WriteLine($"结果为:{result}"); } catch (Exception ex) { WriteLine($"异常被捕捉: {ex.Message}"); } WriteLine("----------------------------------------------"); WriteLine(); var t1 = new Task<int>(() => TaskMethod("Task 3", 3)); var t2 = new Task<int>(() => TaskMethod("Task 4", 4)); var complexTask = Task.WhenAll(t1, t2); // 通过ContinueWith TaskContinuationOptions.OnlyOnFaulted的方式 如果task出现异常 那么才会执行该方法 var exceptionHandler = complexTask.ContinueWith(t => { WriteLine($"异常被捕捉:{t.Exception.Message}"); foreach (var ex in t.Exception.InnerExceptions) { WriteLine($"-------------------------- {ex.Message}"); } },TaskContinuationOptions.OnlyOnFaulted); t1.Start(); t2.Start(); ReadLine(); } static int TaskMethod(string name, int seconds) { WriteLine($"任务运行在{CurrentThread.ManagedThreadId}上. 是否为线程池线程:{CurrentThread.IsThreadPoolThread}"); Sleep(TimeSpan.FromSeconds(seconds)); // 人为抛出一个异常 throw new Exception("Boom!"); return 42 * seconds; }