3

我正在按照此处的示例代码来了解异步任务。我修改了代码以编写任务工作与主要工作的一些输出。输出将如下所示:

在此处输入图像描述

我注意到,如果我删除 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();
   }
}
4

3 回答 3

6

当异常被 抛出时,它会从您的委托中ThrowIfCancellationRequested传播出去。Task此时,它被框架捕获并添加到该Task. 同时,也Task就是过渡到Faulted状态。

理想情况下,您希望观察所有Task异常。如果您将Tasks 用作基于任务的异步模式的一部分,那么在某些时候您应该await使用Task,它会在Task. 如果您将Tasks 用作任务并行库的一部分,那么在某些时候您应该调用Waitor Task<T>.Result,它会传播 , 上的所有异常Task,包裹在AggregateException.

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

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

于 2013-04-25T20:52:50.877 回答
1

如果您不等待任务完成,您的程序将继续运行,因此您将通过 catch-block。

另一方面,如果您要await执行任务,则仍然会捕获异常

于 2013-04-25T18:35:16.630 回答
1

如果您不等待task完成,您的程序将在抛出异常之前退出(或至少离开该执行区域)。

如果主线程已经在

Console.ReadKey();

它不会“备份”到try/catch块。

于 2013-04-25T18:35:32.677 回答