-2

I am following the pattern specified here by Microsoft. https://msdn.microsoft.com/en-us/library/hh191443.aspx

The article walks through developing async code using await. However, they do not talk about what happens when there is an exception before the await returns.

In the example below the outside exception ends the parent thread and the task has no thread to come back to. How do I handle this scneario?

class Program
    {
        private static void Main(string[] args)
        {
            CallAsync();
            Console.ReadKey();
        }

        public static async void CallAsync()
        {
            var task = CallExceptionAsync();
            ThrowException("Outside");
            await task;
        }

        public static Task CallExceptionAsync()
        {
            return Task.Run(() =>
            {
                ThrowException("Inside");
            });

        }

        public static void ThrowException(string msg)
        {
            throw new Exception(msg);
        }        
    }
Anish
  • 3,045
  • 3
  • 27
  • 29

2 Answers2

0

You have to be prepared to handle an exception that may have been percolated back to your main thread by surrounding that call with a try/catch. In that case you should receive an AggregateException that wraps one or more exceptions from the async thread you started.

Jeff Prince
  • 658
  • 6
  • 13
  • I misread your example a bit. What I said is true for the second thread you are starting, but you are also throwing an exception at the main thread level. What is your point? If you don't have a catch block for that main thread exception, it is going to be terminated (or caught by some other code higher up). Are you asking what will happen with the second thread if the first thread terminates before it? – Jeff Prince Sep 01 '15 at 20:54
  • my example is a dumbed down example. My actual code is more complicated and I was able to duplicate the same behavior here. In my original code, I am making a call out to a web service, also saving to a database. Both are throwing exceptions. I am able to duplicate that in a much less complex example here. It doesn't matter whether it is in main or not with a .wait(). The behavior is still thes ame – Anish Sep 01 '15 at 20:55
  • I'll throw out another question for comment here: my understanding is that if the main thread terminates, all of its child threads are terminated at the same time. Is this true? – Jeff Prince Sep 01 '15 at 20:56
  • @Anish What is the "behavior" you are referring to? – Jeff Prince Sep 01 '15 at 20:57
  • This posting seems to indicate that the main thread (and process) would be terminated along with all its "child" threads if the main thread was where the main() method was locoated, but that if it wasn't, all the "child" threads would continue to execute. http://stackoverflow.com/questions/4666628/do-child-threads-exit-when-the-parent-thread-terminates – Jeff Prince Sep 01 '15 at 21:00
  • The behavior: the outside exception gets thrown and control goes back to the caller, the inside exception kills my app. – Anish Sep 01 '15 at 21:00
  • @Anish Not sure what you mean by outside/inside exception. Either way the result is the same: if you throw an exception on the main thread without catching it, the process is going to terminate, including all threads you may have created. If you throw an exception in the thread you created and have no exception handler, I think you are going to again cause the process to terminate. Maybe some one can comment on this. Do tasks that throw an unhandled exception percolate back to creating thread, or does the task just return an error with some sort of diagnostic trail to what exception was? – Jeff Prince Sep 01 '15 at 21:06
  • I think this article provides a good answer to the question I asked about how Task sends exceptions back to the main thread: https://msdn.microsoft.com/en-us/library/dd997415(v=vs.110).aspx. – Jeff Prince Sep 01 '15 at 21:10
  • I don't think my question is clear. What I am asking is.. If I have code like this. var task = asyncMethod(); ..some stuff here; ..an exception gets called here; await task(); what will happen? How can recover from this elegantly. My task is now abandoned. Any exceptions inside my task gets lost. In my app, those exceptions are killing my app. What I have posted here is a simplified example. It has nothing to do with being executed in a console app. – Anish Sep 01 '15 at 21:22
0

That's exactly why you shouldn't be using async void. In a regular async method exceptions thrown from the method's body are captured and stored on the returned task. Since in the case of async void the caller has no task to observe the exception brings down the process.

So, you should return a task, and in the case of Main you can block synchronously with Task.Wait:

private static void Main()
{
    CallAsync().Wait();
}

public static async Task CallAsync()
{
    var task = CallExceptionAsync();
    ThrowException("Outside");
    await task;
}

Blocking with Task.Wait is not an appropriate solution for a real app, since it can cause deadlocks where SynchronizationContext is involved. In that case you can use Stephen Cleary's AsyncContext.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • Can you go a little more into AsyncContext? – Anish Sep 01 '15 at 21:01
  • @Anish It provides a `SynchronizationContext` for console apps, that don't have any (unlike UI apps). But that's not really the point. The point is to avoid `async void`. `async void` is only appropriate for event handlers. – i3arnon Sep 01 '15 at 21:03