关于异步任务,为什么要捕获OperationCanceledException需要Wait()?

Vim*_*mes 3 c# asynchronous exception-handling .net-4.0 task-parallel-library

我在这里遵循示例代码来了解异步任务.我修改了代码来编写任务工作与主要工作的一些输出.输出将如下所示:

在此输入图像描述

我注意到,如果我删除Wait()调用,程序运行相同,除了我无法捕获任务被取消时抛出的异常.有人可以解释在需要Wait()的幕后发生了什么才能击中catch块吗?

一个警告,Visual Studio调试器将错误地停止Console.WriteLine(" - task work");在行上,并显示消息"OperationCanceledException未被用户代码处理".当发生这种情况时,只需单击继续或按F5查看程序的其余部分.有关详细信息,请参见http://blogs.msdn.com/b/pfxteam/archive/2010/01/11/9946736.aspx.

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();
   }
}
Run Code Online (Sandbox Code Playgroud)

Ste*_*ary 6

抛出异常时ThrowIfCancellationRequested,它会从您的Task委托中传播出来.此时,它被框架捕获并添加到异常列表中Task.与此同时,这Task又转变为Faulted国家.

理想情况下,您希望观察所有Task异常.如果您使用TaskS作为基于任务的异步模式的一部分,那么在某些时候,你应该awaitTask,其传播的第一个例外Task.如果您使用TaskS作为任务并行库的一部分,那么在某些时候,你应该打电话Wait或者Task<T>.Result,它传播的所有的异常Task,包裹在一个AggregateException.

如果您没有观察到Task异常,那么当Task最终确定时,运行时将引发TaskScheduler.UnobservedTaskException然后忽略该异常.(这是.NET 4.5的行为; 4.5之前的行为会引发UnobservedTaskException然后终止进程).

在您的情况下,您不必等待任务完成,因此退出try/catch块.一段时间后,会UnobservedTaskException被引发,然后忽略异常.