Exception handling for asynchronous methods is a little weird.
If I write a synchronous method, it's pretty clear. For example, if I do this:
public static void Test1()
{
ThrowException("Test1 has a problem");
}
public static void ThrowException(string message)
{
throw new MyException(message.ToUpper());
}
I get an exception that is relatively easy to read:
MyException thrown. Message: TEST1 HAS A PROBLEM
Trace: at Program.ThrowException(String message)
at Program.Test1()
at Program.Main()
But if I do something very similar, but async:
public static async Task Test2()
{
await ThrowExceptionAsync("Test2 has a problem");
}
public static async Task ThrowExceptionAsync(string message)
{
throw new MyException(message.ToUpper());
}
I get this mess:
System.AggregateException thrown. Message: One or more errors occurred.
Trace: at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at Program.Main()
MyException thrown. Message: TEST2 HAS A PROBLEM
Trace: at Program.<ThrowExceptionAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Program.<Test2>d__1.MoveNext()
MyException thrown. Message: TEST2 HAS A PROBLEM
Trace: at Program.<ThrowExceptionAsync>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
at Program.<Test2>d__1.MoveNext()
This presents two problems:
I can't write a catch
block for MyException
because it gets wrapped in an AggregateException. In other words, the following does not work:
try { Test2().Wait(); } catch (MyException me) { //This never fires } catch(System.AggregateException e) { //It'll go here instead }
Is there a standard way of dealing with this? I'd sort of would like the exception to be easier to read, and I really would like to be able to catch MyException
instead of catching AggregateException
and searching for it.
You have two options:
await
. If you're using ASP.NET, there is almost never a reason to not use async
/ await
through the whole stack. .GetAwaiter().GetResult()
. It will get the same value as .Result
, but it will also unwrap an exception. await
with asynch
methods: it will unpack the exception, save you from the deadlock and inefficient code. Deadlock off-topic: async
method captures current synchronization context so if you'll try to wait for the completion in the same synchronization context, you'll get a deadlock.
public partial class Form1 : Form
{
public Form1() => InitializeComponent();
private void button1_Click(object sender, EventArgs e)
=> doSomthing().Wait();
private async Task doSomthing() => await Task.Delay(1000);
}
To do not capture the context you can use ConfigureAwait
:
private void button1_Click(object sender, EventArgs e)
=> doSomthing()
.ConfigureAwait(continueOnCapturedContext: false)
.GetAwaiter()
.GetResult();
private async Task doSomthing() => await Task.Delay(1000);
This article covers this topic in depth, feel free to read it if you are interested in the pitfalls of asynch
/ await
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.