Exception Handling in async/await in C # (I)

Source: Internet
Author: User

In Synchronous Programming, once an error occurs, an exception is thrown. We can use try... Catch is used to catch exceptions, but uncaptured exceptions are continuously passed up to form a simple and unified error handling mechanism. However, for asynchronous programming, exception handling has always been troublesome, which is also one of the advantages of async, await, Jscex, and other asynchronous programming models in C. However, synchronous error handling mechanisms cannot completely avoid asynchronous error handling methods, which must be guaranteed by practice specifications, at least we need to understand how async/await captures and distributes exceptions. During the development of Jscex, I also learned a lot about the TPL and C # asynchronous features in the C # Internal email list. error handling is also one of them. Record it here.
Use try... Catch exception
First, let's take a look
Static async Task ThrowAfter (int timeout, Exception ex)
{
Await Task. Delay (timeout );
Throw ex;
}

Static void PrintException (Exception ex)
{
Console. WriteLine ("Time: {0} \ n {1} \ n ============", _ watch. Elapsed, ex );
}

Static Stopwatch _ watch = new Stopwatch ();

Static async Task MissHandling ()
{
Var t1 = ThrowAfter (1000, new NotSupportedException ("Error 1 "));
Var t2 = ThrowAfter (2000, new NotImplementedException ("Error 2 "));

Try
{
Await t1;
}
Catch (NotSupportedException ex)
{
PrintException (ex );
}
}

Static void Main (string [] args)
{
_ Watch. Start ();

MissHandling ();

Console. ReadLine ();
}
The output of this Code is as follows:
Time: 00:00:01. 2058970
System. NotSupportedException: Error 1
At AsyncErrorHandling. Program. d _ 0. MoveNext () in... \ Program. cs: line 16
--- 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 AsyncErrorHandling. Program. d _ 3. MoveNext () in... \ Program. cs: line 33
================
In the MissingHandling method, we first use the ThrowAfter method to start two tasks, which will throw two different exceptions after one second and two seconds respectively. However, in the next try, we only perform await operations on t1. It is easy to understand that the NotSupportedException thrown by t1 will be caught by catch, which takes about 1 second-of course, as can be seen from the above data, in fact, when t1 was captured, it took 1.2 of the time and the error was large. This is because the program has just started, and the TPL is in the "warm-up" status, there will be a great deal of sales in scheduling. Another problem is worth noting: Where is the NotImplementedException that t2 throws in two seconds?
Uncaptured exceptions
C #'s async/await functions are based on TPL Task objects. Each await operator is "Waiting" for a Task to complete. In the previous (or present) TPL, The destructor of the Task object will check whether its Exception object has been "accessed". If not, and the Task object has encountered an Exception, this exception will be thrown, and the final result is usually process exit. Therefore, we must carefully handle errors of each Task object and avoid omission. In. in NET 4.5, this behavior has been changed. For any exceptions that have not been checked, TaskSchedular is triggered. unobservedTaskException event-if you do not listen to this event, uncaptured exceptions will disappear.
Therefore, we have made a simple transformation to the Main method.
Static void Main (string [] args)
{
Taskschedtion. UnobservedTaskException + = (_, ev) => PrintException (ev. Exception );

_ Watch. Start ();

MissHandling ();

While (true)
{
Thread. Sleep (1000 );
GC. Collect ();
}
}
There are two points for transformation. One is to respond to taskschedexception. UnobservedTaskException, Which is needless to say. Another point is to continuously trigger garbage collection so that the Finalizer thread can call the destructor. In addition to printing the previous information, this code also outputs the following content:
Time: 00:00:03. 0984560
System. aggresponexception: A Task's exception (s) were not observed either by Waiting on the Task or accessing its Exception property. as a result, the unobserved exception was rethrown by the finalizer thread. ---> System. notImplementedException: Error 2
At AsyncErrorHandling. Program. d _ 0. MoveNext () in... \ Program. cs: line 16
--- End of inner exception stack trace ---
---> (Inner Exception #0) System. NotImplementedException: Error 2
At AsyncErrorHandling. Program. d _ 0. MoveNext () in... \ Program. cs: line 16 <---
================
From the above information, we can see that the UnobservedTaskException event is not triggered immediately after an exception is thrown, but is triggered and executed in a garbage collection process from the Finalizer thread. It is not difficult to draw the conclusion that the response method of the event cannot be too time-consuming or congested, or it will have a catastrophic impact on program performance.
So what if we want to handle exceptions thrown in t1 and t2 at the same time? Now it is time for the Task. WhenAll method to play:
Static async Task BothHandled ()
{
Var t1 = ThrowAfter (1000, new NotSupportedException ("Error 1 "));
Var t2 = ThrowAfter (2000, new NotImplementedException ("Error 2 "));

Try
{
Await Task. WhenAll (t1, t2 );
}
Catch (NotSupportedException ex)
{
PrintException (ex );
}
}
If you execute this code, you will find that the output is the same as that of the first code, but the difference is that the t2 exception in the first code is "omitted, at present, the t1 and t2 exceptions of this Code are captured, but the await statement only throws "one of them" exceptions.
WhenAll is an auxiliary method. Its input is n Task objects, and the output is a Task object that returns their result arrays. The new Task object is completed only after all input is "finished. Here, "end" means success and failure (cancellation is also a type of failure, that is, OperationCanceledException is thrown ). In other words, if a Task object in the n inputs quickly fails, the new Task object must be completed after all other input objects succeed or fail. After the new Task object is completed, there may be two manifestations:
• If all input Task objects are successful, their result arrays are returned.
• If at least one input Task object fails, an "one of them" exception is thrown.
It is not necessary to say that all the successes are successful. In the case of failure, what is an "one of them" exception thrown? What if we want to handle all thrown exceptions? We will continue to discuss this issue next time.

 
From Lao Zhao's point of view-pursuing the beauty of Programming

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.