如何处理Task.Runexception

我从Task.Run中捕获exception时遇到了问题。我更改了代码并解决了问题。 我愿意弄清楚在这两种方式下处理Task.Run中的exception有什么区别:

在Outside函数中我无法捕获exception但在Inside中我可以捕获它。

void Outside() { try { Task.Run(() => { int z = 0; int x = 1 / z; }); } catch (Exception exception) { MessageBox.Show("Outside : " + exception.Message); } } void Inside() { Task.Run(() => { try { int z = 0; int x = 1 / z; } catch (Exception exception) { MessageBox.Show("Inside : "+exception.Message); } }); } 

运行任务时,如果某些内容等待任务的结果或任务完成,则会保留并重新抛出它抛出的任何exception。

Task.Run()返回一个可用于执行此操作的Task对象,因此:

 var task = Task.Run(...) try { task.Wait(); // Rethrows any exception(s). ... 

对于较新版本的C#,您可以使用await而不是任务.Wait():

 try { await Task.Run(...); ... 

这更整洁。


为了完整起见,这是一个可编译的控制台应用程序,演示了使用await

 using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleApp1 { class Program { static void Main() { test().Wait(); } static async Task test() { try { await Task.Run(() => throwsExceptionAfterOneSecond()); } catch (Exception e) { Console.WriteLine(e.Message); } } static void throwsExceptionAfterOneSecond() { Thread.Sleep(1000); // Sleep is for illustration only. throw new InvalidOperationException("Ooops"); } } } 

使用Task.Wait的想法会做到这一点,但会导致调用线程(如代码所示)等待并因此阻塞,直到任务完成,这有效地使代码同步而不是异步。

而是使用Task.ContinueWith选项来实现结果:

 Task.Run(() => { //do some work }).ContinueWith((t) => { if (t.IsFaulted) throw t.Exception; if (t.IsCompleted) //optionally do some work); }); 

如果任务需要在UI线程上继续,请将TaskScheduler.FromCurrentSynchronizationContext()选项作为参数继续使用,如下所示:

 ).ContinueWith((t) => { if (t.IsFaulted) throw t.Exception; if (t.IsCompleted) //optionally do some work); }, TaskScheduler.FromCurrentSynchronizationContext()); 

此代码将简单地从任务级别重新抛出聚合exception。 当然,您也可以在此处介绍其他forms的exception处理。

在您的外部代码中,您只检查启动任务是否不会抛出exception而不是任务的正文本身。 它以异步方式运行,然后启动它的代码。

您可以使用:

 void Outside() { try { Task.Run(() => { int z = 0; int x = 1 / z; }).GetAwaiter().GetResult(); } catch (Exception exception) { MessageBox.Show("Outside : " + exception.Message); } } 

使用.GetAwaiter().GetResult()等待任务结束并按原样传递抛出的exception,并且不将它们包装在AggregateException

对我来说,我希望我的Task.Run在出错后继续运行,让UI处理错误,因为它有时间。

我的(奇怪的?)解决方案是运行Form.Timer。 我的Task.Run有它的队列(对于长时间运行的非UI东西),我的Form.Timer有它的队列(用于UI的东西)。

由于此方法已经适用于我,因此添加error handling是微不足道的:如果task.Run收到错误,它会将错误信息添加到Form.Timer队列,该队列显示错误对话框。

您可以等待,然后exception冒泡到当前的同步上下文(请参阅Matthew Watson的回答)。 或者,正如Menno Jongerius所提到的,您可以使用ContinueWith来使代码保持异步。 请注意,只有在使用OnlyOnFaulted continuation选项引发exception时才能这样做:

 Task.Run(()=> { //.... some work.... }) // We could wait now, so we any exceptions are thrown, but that // would make the code synchronous. Instead, we continue only if // the task fails. .ContinueWith(t => { // This is always true since we ContinueWith OnlyOnFaulted, // But we add the condition anyway so resharper doesn't bark. if (t.Exception != null) throw t.Exception; }, default , TaskContinuationOptions.OnlyOnFaulted , TaskScheduler.FromCurrentSynchronizationContext());