35

Everywhere I read it says the following code should work, but it doesn't.

public async Task DoSomething(int x)
{
   try
   {
      // Asynchronous implementation.
      await Task.Run(() => {
      throw new Exception();
      x++;
      });
   }
   catch (Exception ex)
   {
      // Handle exceptions ?
   }
}

That said, I'm not catching anything and get an "unhandled exception" originating at the 'throw' line. I'm clueless here.

Brian
  • 5,069
  • 7
  • 37
  • 47
dror
  • 3,759
  • 6
  • 31
  • 45
  • 3
    Because it is being run in the context of another thread. – OldProgrammer Nov 08 '13 at 17:57
  • 1
    @OldProgrammer: Yes, but it should result in a failed task. – SLaks Nov 08 '13 at 17:57
  • 2
    Where have you read that this will work? – Mike Christensen Nov 08 '13 at 17:58
  • 5
    This works for me. You may be seeing the handled exception in the debugger. – SLaks Nov 08 '13 at 17:59
  • @MikeChristensen: Try it; it does work. `Task.Run()` catches the exception and returns a failed task, and `await` handles the failure and jumps to the `catch` block. – SLaks Nov 08 '13 at 17:59
  • 1
    Your code won't even compile, because `x++` is unreachable. – Jon Skeet Nov 08 '13 at 18:00
  • 5
    @JonSkeet Unreachable code is a only warning, not a error. – Scott Chamberlain Nov 08 '13 at 18:02
  • 3
    @ScottChamberlain: Doh, you're right. Oops :) (It's an error in Java... that's all I can say in my defence, feeble as it is. It clearly came up only as a warning, I just didn't read it properly!) – Jon Skeet Nov 08 '13 at 18:02
  • @wilenx Is this your _actual_ code? Please see my answer in case it's not. – ken2k Nov 08 '13 at 18:13
  • @ScottChamberlain Some people may have options set on the compiler that treat warnings as errors, so the statement is valid in some cases. – Michael J. Gray Nov 08 '13 at 18:19
  • I am produced with a warning. But that's fine, because I'm interested in the catching. – dror Nov 08 '13 at 18:39
  • @Mike Christensen, see http://msdn.microsoft.com/en-us/magazine/jj991977.aspx Figure 4 for example. It doesn't work in my WinForms project. – dror Nov 08 '13 at 18:41
  • 2
    There sure are a lot of people saying "but it works!!!" when it doesn't. I run this code and the debugger stops on the exception saying it's unhandled, just like the OP says. (And seriously, folks, stop wasting our time complaining about a line of unreachable code.) – Glenn Maynard May 26 '14 at 20:14

5 Answers5

39

You have the "Just my code" Option turned on. With this on, it is considering the exception unhandled with respect to "just your code"--because other code is catching the exception and stuffing it inside of a Task, later to be rethrown at the await call and caught by your catch statement.

Without being attached in the debugger, your catch statement will be triggered, and it will run as you expect. Or you can just continue from within the debugger and it will run as expected.

The better thing to do is to just turn off "Just my code". IMO, it causes more confusion than it is worth.

Matt Smith
  • 17,026
  • 7
  • 53
  • 103
  • 6
    Hey, a real answer (among a bunch of people going "it works for me" as if they think that helps anybody). There sure are a lot of options in VS these days that cause huge, confusing problems ("enable native code debugging" is another). – Glenn Maynard May 26 '14 at 20:17
  • This helped me with a related problem where the debugger was catching an exception at the await rather than deeper in the call stack where my real issue was occuring. – Tom Leys Sep 28 '16 at 20:25
16

As SLaks said, your code works fine.

I strongly suspect you over-simplified your example, and have an async void in your code.

The following works fine:

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

public static async void CallAsync()
{
    try
    {
        await DoSomething();
    }
    catch (Exception)
    {
        // Handle exceptions ?
        Console.WriteLine("In the catch");
    }
}

public static Task DoSomething()
{
    return Task.Run(() =>
    {
        throw new Exception();
    });
}

The following doesn't work:

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

public static void CallAsync()
{
    try
    {
        DoSomething();
    }
    catch (Exception)
    {
        // Handle exceptions ?
        Console.WriteLine("In the catch");
    }
}

public static async void DoSomething()
{
    await Task.Run(() =>
    {
        throw new Exception();
    });
}

See http://msdn.microsoft.com/en-us/magazine/jj991977.aspx

Async void methods have different error-handling semantics. When an exception is thrown out of an async Task or async Task method, that exception is captured and placed on the Task object. With async void methods, there is no Task object, so any exceptions thrown out of an async void method will be raised directly on the SynchronizationContext that was active when the async void method started. Figure 2 illustrates that exceptions thrown from async void methods can’t be caught naturally.

ken2k
  • 48,145
  • 10
  • 116
  • 176
8

Your code won't even compile cleanly at the moment, as the x++; statement is unreachable. Always pay attention to warnings.

However, after fixing that, it works fine:

using System;
using System.Threading.Tasks;

class Test
{
    static void Main(string[] args)
    {
        DoSomething(10).Wait();
    }

    public static async Task DoSomething(int x)
    {
        try
        {
            // Asynchronous implementation.
            await Task.Run(() => {
                throw new Exception("Bang!");
            });
        }
        catch (Exception ex)
        {
            Console.WriteLine("I caught an exception! {0}", ex.Message);
        }
    }
}

Output:

I caught an exception! Bang!

(Note that if you try the above code in a WinForms app, you'll have a deadlock because you'd be waiting on a task which needed to get back to the UI thread. We're okay in a console app as the task will resume on a threadpool thread.)

I suspect the problem is actually just a matter of debugging - the debugger may consider it unhandled, even though it is handled.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
-1

Instead of using await, access the Task.Result property and put a try and catch around that access. You can also follow the example here and try that style.

Keep in mind that all exceptions thrown inside the context of a task thread are wrapped in an AggregateException.

Michael J. Gray
  • 9,784
  • 6
  • 38
  • 67
  • 1
    No; `await` works fine here. You should never use `Task.Result`; it's a synchronous call that can easily deadlock. – SLaks Nov 08 '13 at 18:00
  • @SLaks `await` really only captures the thread ID and sets up a signal on `Task.Result` being assigned by the underlying thread pool thread. Once the result is assigned (returned), it has some logic to decide if it needs to form a continuation or return the result back to the original thread's stack. Accessing `Task.Result` is synchronous, but it does not "deadlock" per se, it blocks. Besides, the point is to get this working for them in any way possible, not necessarily the perfect way. It's simply a solution and not a best practice. – Michael J. Gray Nov 08 '13 at 18:02
  • 3
    @MichaelJ.Gray True, but if there is a synchronization context that is captured and the async method actually does an asynchronous action then you are blocking continuations from running while waiting for the continuations to run, causing a deadlock. This is *very* easy to do in any async method. In addition to defeating the entire purpose of having an `async` method in the first place. If you're going to do blocking waits you may as well just make the method synchronous so as to not lie to your callers. Also `await` will both unwrap and re-throw any exceptions from the awaited operation. – Servy Nov 08 '13 at 18:04
  • @Servy So you are saying that if you read `Task.Result` and the parent task method invokes another `async` method and awaits on the value, it will cause a deadlock? Remember that `await` blocks or creates a continuation. Often times I find that it's not even asynchronous and just returns immediately (synchronously, even), especially if you have quick resource access or are doing CPU bound work under little to no load. – Michael J. Gray Nov 08 '13 at 18:05
  • 1
    @MichaelJ.Gray No, I'm saying that if you call `Result` on the task, and that task calls `await` internally, then your code will deadlock, because that task you are waiting on has a scheduled continuation that can't run because you're blocking the current synchronization context. This is like await 101...there are a million articles about it. – Servy Nov 08 '13 at 18:07
  • @Servy Actually it works just fine. Here is a test case: http://stackoverflow.com/questions/19865823/ – Michael J. Gray Nov 08 '13 at 18:17
  • @MichaelJ.Gray That's being run from a console app that has no synchronization context. If you ran it from any UI based app that *had* a synchronization context it would deadlock. – Servy Nov 08 '13 at 18:21
  • @Servy I tested it in ASP .NET MVC 5 and it works fine as well. – Michael J. Gray Nov 08 '13 at 18:25
  • I'm not familiar enough with MVC to speak about it, if it has any intricacies, nor do I have your exact code to look at. See the linked article on the topic, or just google "await deadlock" for a ton of examples as to how this exact code *can* deadlock, even if it can work in certain limited situations. – Servy Nov 08 '13 at 18:29
  • @MichaelJ.Gray: ASP.Net's SyncContext is also multi-threaded. That will deadlock in WinForms or WPF. – SLaks Nov 08 '13 at 18:38
  • @SLaks But they said "any UI based app". A web application has a user interface. I would have argued that console applications do as well, but I figured I would be reasonable and not so pedantic. – Michael J. Gray Nov 08 '13 at 20:20
  • @MichaelJ.Gray: As far as C# is concerned, a web application does not have a UI; it just receives HTTP requests. – SLaks Nov 10 '13 at 00:40
  • @SLaks If you are saying that just because the code processes messages and sends back instructions on how to visualize underlying data, that it doesn't have a UI, then technically no forms application would either. A forms application simply takes a programmer defined set of calls and invokes them and responds to various events and windows messages. C# only allows such behavior and never actually defines how something is rendered, that's left up to the window manager. This is why C# works on more than just Windows. Please don't be that pedantic. – Michael J. Gray Nov 10 '13 at 05:35
-2

The exception is not cuaght. The reason is - when below statement is executed

await Task.Run(() => {
            throw new Exception("Bang!");
        });

its on a separate thread. The exception raised on that thread goes uncaught.

change it to look like as below

await Task.Run(() => {

                try
                {
                    throw new Exception("Bang!");
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);   
                }

        });
  • You're right the exception is raised in another thread, and that thread catches the exception, indicates to the thread running the continuation that the other thread threw an exception, and that results in the exception being re-thrown in the thread that executes the continuation. – Servy Jul 23 '14 at 14:41