我遇到从Task.Run捕获异常的问题我改变了我的代码并解决了我的问题。我愿意弄清楚在这两种方式下处理Task.Run中的异常有什么区别:
在Outside函数中我无法捕获异常,但在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);
}
});
}
答案 0 :(得分:28)
当一个任务运行时,它会抛出任何异常,并在等待任务结果或任务完成时重新抛出。
Task.Run()
会返回一个Task
对象,您可以使用它来执行此操作,因此:
var task = Task.Run(...)
try
{
task.Wait(); // Rethrows any exception(s).
...
对于较新版本的C#,您可以使用await
代替Task.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");
}
}
}
答案 1 :(得分:26)
使用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());
此代码将简单地从任务级别重新抛出聚合异常。当然,你也可以在这里介绍一些其他形式的异常处理。
答案 2 :(得分:3)
在您的外部代码中,您只检查启动任务是否不会抛出异常而不是任务的正文本身。它以异步方式运行,然后启动它的代码。
您可以使用:
void Outside()
{
try
{
Task.Run(() =>
{
int z = 0;
int x = 1 / z;
}).GetAwaiter().GetResult();
}
catch (Exception exception)
{
MessageBox.Show("Outside : " + exception.Message);
}
}
使用.GetAwaiter().GetResult()
等待任务结束并按原样传递抛出的异常,并且不将它们包装在AggregateException
中。
答案 3 :(得分:2)
启用选项“仅我的代码”时,Visual Studio在某些情况下会在引发异常的行上中断,并显示一条错误消息,内容为:
未由用户代码处理的异常。
此错误是良性的。您可以按 F5 继续并查看这些示例中演示的异常处理行为。要防止Visual Studio遇到第一个错误,只需禁用工具>选项>调试>常规”下的“仅我的代码”复选框即可。
答案 4 :(得分:1)
您可以等待,然后异常会上升到当前同步上下文(请参阅Matthew Watson的回答)。或者,如Menno Jongerius所述,您可以SomeModel.find({"a.d": {$gt: value} })
保持代码异步。请注意,只有在使用ContinueWith
延续选项引发异常时,您才能这样做:
OnlyOnFaulted
答案 5 :(得分:0)
对我来说,我希望我的Task.Run在出错后继续运行,让UI处理错误,因为它有时间。
我的(奇怪的?)解决方案是运行Form.Timer。我的Task.Run有它的队列(对于长时间运行的非UI东西),我的Form.Timer有它的队列(用于UI的东西)。
由于此方法对我来说已经起作用,因此添加错误处理是微不足道的:如果task.Run收到错误,它会将错误信息添加到Form.Timer队列,该队列将显示错误对话框。
答案 6 :(得分:-2)
在@MennoJongerius的基础上回答以下问题,以保持异步,并将异常从.wait移至Async Completed事件处理程序:
Public Event AsyncCompleted As AsyncCompletedEventHandler
).ContinueWith(Sub(t)
If t.IsFaulted Then
Dim evt As AsyncCompletedEventHandler = Me.AsyncCompletedEvent
evt?.Invoke(Me, New AsyncCompletedEventArgs(t.Exception, False, Nothing))
End If
End Sub)