多线程使用过程中,除了线程同步的问题要考虑外,异常处理也是经常要面对的事情。

默认主线程捕获不到异步线程的异常

如下代码:

 1 namespace ConsoleApplication29
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             //异步线程的异常处理
 8             try
 9             {
10                 Task.Factory.StartNew(() => 
11                 {
12                     throw new Exception("异步线和发生异常了!");
13                 });
14             }
15             catch (Exception ex)
16             {
17                 //这里是捕获不到的
18                 Console.WriteLine(ex.ToString());
19             }
20 
21             Console.ReadKey();
22         }
23     }
24 }
View Code

常用的异常处理方法

1,在异步线程内部使用try/catch

如下代码:

 1 namespace ConsoleApplication29
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             //异步线程的异常处理
 8             //1,在异步线程内部使用try/catch
 9             Task.Factory.StartNew(() =>
10             {
11                 try
12                 {
13                     throw new Exception("异步线和发生异常了!");
14                 }
15                 catch (Exception ex)
16                 {
17                     Console.WriteLine(ex.ToString());
18                 }
19             });
20 
21             Console.ReadKey();
22         }
23     }
24 }
View Code

 运行结果:

2,调用Task的Wait方法

 如下代码:

注意:

除了调用Task的Wait方法后,在主线程可以捕获异常外,对于有返回值的Task,只要接收了它的返回值就不再需要调用Wait方法了。

 1 namespace ConsoleApplication29
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             //异步线程的异常处理
 8             //2,调用Task的Wait方法
 9             try
10             {
11                 var task = Task.Factory.StartNew(() =>
12                 {
13                     throw new Exception("异步线和发生异常了!");
14                 });
15                 task.Wait();
16             }
17             catch (Exception ex)
18             {
19                 Console.WriteLine(ex.ToString());
20             }
21 
22             Console.ReadKey();
23         }
24     }
25 }
View Code

 

运行结果:

3,在Task的ContinueWith方法中读取Task的Exception属性

如下代码:

 1 namespace ConsoleApplication29
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             //异步线程的异常处理
 8             //3,在Task的ContinueWith方法中读取Task的Exception属性
 9             Task.Factory.StartNew(() =>
10             {
11                 throw new Exception("异步线和发生异常了!");
12             }).ContinueWith(t => 
13             {
14                 if (t.IsFaulted)
15                 {
16                     Console.WriteLine(t.Exception.InnerException);
17                 }
18             }, TaskContinuationOptions.OnlyOnFaulted);
19 
20             Console.ReadKey();
21         }
22     }
23 }
View Code

 

运行结果:

 4,全局设置TaskScheduler.UnobservedTaskException

 如果异步线程里的异常没有被处理,也没有调用Task.Wait方法将异常传给主线程处理,最严重的后果可能会导致整个应用程序奔溃。详细原因参考:System.Threading.Tasks.Task引起的IIS应用程序池崩溃

所以,为了保证应用程序不会因为异步线程的异常未被处理导致挂掉,推荐的做法是,全局设置TaskScheduler.UnobservedTaskException。

如果是web程序,可以在Global的Application_Start事件中进行设置,如下代码:

1 protected override void Application_Start(object sender, EventArgs e)
2 {
3     System.Threading.Tasks.TaskScheduler.UnobservedTaskException += (s, v) =>
4     {
5         v.SetObserved();
6     };
7 }
View Code