如何处理任务.运行异常
本文关键字:任务 运行 异常 处理 何处理 | 更新日期: 2023-09-27 18:08:22
我在捕获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);
}
});
}
使用Task的思路。Wait可以达到目的,但会导致调用线程等待(如代码所说),因此阻塞直到任务完成,这有效地使代码同步而不是异步。
请使用Task。选项来实现结果:
Task.Run(() =>
{
//do some work
}).ContinueWith((t) =>
{
if (t.IsFaulted) throw t.Exception;
if (t.IsCompleted) //optionally do some work);
});
如果任务需要在UI线程上继续,使用TaskScheduler.FromCurrentSynchronizationContext()选项作为continue的参数,如下所示:
).ContinueWith((t) =>
{
if (t.IsFaulted) throw t.Exception;
if (t.IsCompleted) //optionally do some work);
}, TaskScheduler.FromCurrentSynchronizationContext());
这段代码将简单地从任务级别重新抛出聚合异常。当然,您也可以在这里引入一些其他形式的异常处理。
当任务运行时,它抛出的任何异常都将被保留,并在等待任务的结果或任务完成时重新抛出。
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");
}
}
}
在您的Outside代码中,您只检查启动任务是否不会抛出异常,而不是任务本身。它是异步运行的,并且启动它的代码将在此时完成。
你可以使用:
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
中。
您可以等待,然后异常会冒泡到当前同步上下文(参见Matthew Watson的回答)。或者,正如Menno Jongerius提到的,您可以使用ContinueWith
来保持代码异步。请注意,只有在使用OnlyOnFaulted
continuation选项抛出异常时才能这样做:
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());
当选项"Just My Code"被启用时,Visual Studio在某些情况下会在抛出异常的那一行中断,并显示一条错误消息:
用户代码未处理的异常。
此错误是良性的。可以按F5继续查看这些示例中演示的异常处理行为。为了防止Visual Studio在第一个错误时中断,只需禁用Tools> Options> Debugging> General下的just My Code复选框。 对于我来说,我想要我的任务。在出现错误后继续运行,让UI在有时间时处理错误。 我的(奇怪的?)解决方案是也有一个表单。计时器运行。我的任务。Run有它的队列(用于长时间运行的非ui的东西),和我的表单。计时器有它的队列(用于UI的东西)。 因为这个方法已经为我工作了,所以添加错误处理是微不足道的:Run得到一个错误,它将错误信息添加到Form中。计时器队列,它显示错误对话框。 基于@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)