Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Regarding asynchronous Task, why is a Wait() required to catch OperationCanceledException?

I'm following the example code here to learn about asynchronous tasks. I've modified the code to write some output of the task's work vs. the main work. The output will look like this:

enter image description here

I noticed that if I remove the Wait() call, the program runs the same except I can't catch the exception that's thrown when the task is canceled. Can someone explain what's going on behind the scenes requiring the Wait() in order to hit the catch block?

One warning, the Visual Studio debugger will erroneously stop on the Console.WriteLine(" - task work"); line with the message "OperationCanceledException was unhandled by user code". When that happens, just click Continue or hit F5 to see the rest of the program run. See http://blogs.msdn.com/b/pfxteam/archive/2010/01/11/9946736.aspx for details.

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApplication1
class Program
{
  static void Main()
  {
     var tokenSource = new CancellationTokenSource();
     var cancellationToken = tokenSource.Token;

    // Delegate representing work that the task will do.
     var workDelegate 
            = (Action)
              (
                () =>
                   {
                       while (true)
                       {
                          cancellationToken.ThrowIfCancellationRequested(); 
              // "If task has been cancelled, throw exception to return"

          // Simulate task work
                  Console.WriteLine(" - task work"); //Visual Studio  
           //erroneously stops on exception here. Just continue (F5). 
           //See http://blogs.msdn.com/b/pfxteam/archive/2010/01/11/9946736.aspx
                          Thread.Sleep(100);
                       }
                   }
              );


     try
     {
     // Start the task
         var task = Task.Factory.StartNew(workDelegate, cancellationToken);

      // Simulate main work
         for (var i = 0; i < 5; i++)
         {
             Console.WriteLine("main work");
             Thread.Sleep(200);
         }

       // Cancel the task
         tokenSource.Cancel();

       // Why is this Wait() necessary to catch the exception?
       // If I reomve it, the catch (below) is never hit, 
       //but the program runs as before.
          task.Wait();
     }
     catch (AggregateException e)
     {
         Console.WriteLine(e.Message);
         foreach (var innerException in e.InnerExceptions)
         Console.WriteLine(innerException.Message);
     }

     Console.WriteLine("Press any key to exit...");
     Console.ReadKey();
   }
}
like image 929
Vimes Avatar asked Nov 23 '25 03:11

Vimes


1 Answers

When the exception is thrown by ThrowIfCancellationRequested, it propagates out of your Task delegate. At that point, it is captured by the framework and added to the list of exceptions for that Task. At the same time, that Task is transitioned to the Faulted state.

Ideally, you want to observe all your Task exceptions. If you're using Tasks as part of the task-based asynchronous pattern, then at some point you should await the Task, which propagates the first exception on the Task. If you're using Tasks as part of the task parallel library, then at some point you should call Wait or Task<T>.Result, which propagates all the exceptions on the Task, wrapped in an AggregateException.

If you do not observe a Task exception, then when the Task is finalized, the runtime will raise TaskScheduler.UnobservedTaskException and then ignore the exception. (This is the .NET 4.5 behavior; the pre-4.5 behavior would raise UnobservedTaskException and then terminate the process).

In your case, you don't wait for the task to complete, so you exit the try/catch block. Some time later, UnobservedTaskException is raised and then the exception is ignored.

like image 102
Stephen Cleary Avatar answered Nov 25 '25 16:11

Stephen Cleary