18

Does try catch outside of: await Task.Run(() => make sense or just use them only inside of await?

private async void Test()
{
     try
     {
         await Task.Run(() =>
         {
             try
             {
                  DoingSomething();
             }
             catch (Exception ex)
             {
                  log.Error(ex.Message);
             }
         });
      }
      catch (Exception ex)
      {
          log.Error(ex.Message);
      }
}
Dan Homola
  • 3,819
  • 1
  • 28
  • 43
as74
  • 700
  • 4
  • 12
  • 25

4 Answers4

14

If the delegate you pass to Task.Run raises an exception, then you can catch it outside the Task.Run when you await the returned task.

You shouldn't think of await as though it was a block. There's no such thing as "inside of await". Instead, think of await as an operator that takes a single argument (in this case, the Task returned by Task.Run). Task.Run will catch exceptions from its delegate and place them on the returned Task; await will then propagate that exception.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • 4
    You can only catch exception outside if delegate is marked as async ( even if there is no await statement in delegate) `await Task.Run(async () => { throw new Exception("msg"); });` – Arvis Apr 24 '15 at 12:48
  • 2
    @Arvis: No, you can catch it without the `async` inside `Task.Run`. – Stephen Cleary Apr 24 '15 at 22:29
  • 3
    I just ran into this same situation. I could not catch an `SQLException` outside of `Task.Run` unless I added the `async` keyword to my lambda – Jonathon Anderson Jul 14 '17 at 17:18
  • 2
    @NonSecwitter It's probably only the debugger that stops on an exception that "was unhanded by user code". – Clemens Aug 02 '17 at 10:29
14

If you handle Exception inside the delegate (in your case just for logging purpose), await will not raise an exception in normal circumstances. This should be fine.

private async Task Test()
{
         await Task.Run(() =>
         {
             try
             {
                  DoingSomething();
             }
             catch (Exception ex)
             {
                  log.Error(ex.Message);
             }
         });

}

However, since you are awaiting the Task, most probably, there will be some DoSomethingElse in the Test method, which might be affected by the outcome of the Task - in which case it also makes sense to have a try/catch around await.

private async Task Test()
{
     try
     {
         await Task.Run(() =>
         {
             try
             {
                  DoingSomething();
             }
             catch (SomeSpecialException spex)
             {
                  // it is OK to have this exception
                  log.Error(ex.Message);
             }
         });

         DoSomethingElse(); // does not run when unexpected exception occurs.
      }
      catch (Exception ex)
      {
          // Here we are also running on captured SynchronizationContext
          // So, can update UI to show error ....
      }
}
YK1
  • 7,327
  • 1
  • 21
  • 28
  • I tried this in a console app, and put a break in the catch, it was not in the main thread, I throw again and nothing happens. I want the main thread to get the exception. how can I do this? – pogorman Apr 05 '17 at 04:42
  • @pogorman Console app does not have a SynchronizationContext, so code after await can run on any thread. If your actual application is WPF or WinForms, exception would be raised back on main thread – YK1 Apr 05 '17 at 12:21
  • what is the best strategy for a console app? If an exception occurs in the background I want the app to crash hard so that errors are swallowed up silently. Thanks. – pogorman Apr 05 '17 at 13:15
  • ideally I want the app to crash hard on any unexpected exception. – pogorman Apr 05 '17 at 13:28
  • @pogorman, which is default behaviour, unless your thread is marked as background thread – YK1 Apr 05 '17 at 14:19
  • yes - I really want exceptions thrown within a Task.Run to crash the whole app. I can't find a way to do this though on a console app. – pogorman Apr 05 '17 at 14:44
  • @pogorman - exception within `Task.Run` will be raised only when you `await` the `Task` returned from `Task.Run`. If you dont `await` or dont try to get result from that `task` then exception will be swallowed. – YK1 Apr 05 '17 at 22:41
2

You can add try catch to outside code too. The compiler will execute catch section when an exception happens during the async call. Here is more details why would you need try catch around await http://msdn.microsoft.com/en-us/library/vstudio/0yd65esw.aspx look Exceptions in Async Methods

Alexandr Mihalciuc
  • 2,537
  • 15
  • 12
-1

This is the behavior in .Net 6:

try{
 await Task.Run(()=>   & call whatever method    );
}
catch { handle the exception }          <-- the catch will never get hit. An unhandled exception in the Task.Run line will happen

However, this will work:

try{
await Task.Run(async ()=> & call some method );
}
catch(Exception ex){
   handle the exception
}

The async before the ()=> has to be there for .Net 6

I've just confirmed this.

JRrelyea
  • 139
  • 1
  • 4