我正在关注示例代码here以了解异步任务。我已经修改了代码来编写任务工作与主要工作的一些输出。输出将如下所示:
我注意到如果我删除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();
}
}
答案 0 :(得分:6)
ThrowIfCancellationRequested
抛出异常时,它会从Task
代理传播出来。此时,它被框架捕获并添加到Task
的例外列表中。与此同时,Task
已转换为Faulted
州。
理想情况下,您希望观察所有Task
例外情况。如果您使用Task
作为基于任务的异步模式的一部分,那么在某些时候您应该await
Task
,它会传播Task
上的第一个例外}。如果您使用Task
作为任务并行库的一部分,那么在某些时候您应该调用Wait
或Task<T>.Result
,它会传播Task
上的所有例外情况,包裹在AggregateException
。
如果您没有观察到Task
异常,那么当Task
完成后,运行时将引发TaskScheduler.UnobservedTaskException
,然后忽略该异常。 (这是.NET 4.5的行为; 4.5之前的行为会引发UnobservedTaskException
,然后终止进程。)
在您的情况下,您不必等待任务完成,因此退出try / catch块。一段时间后,UnobservedTaskException
被引发,然后忽略该异常。
答案 1 :(得分:1)
如果您不等待任务完成,您的程序将继续运行,因此您将通过catch-block。
另一方面,如果你await
任务,那么异常仍将被捕获
答案 2 :(得分:1)
如果您不等待task
完成,您的程序将在抛出异常之前退出(或至少离开该执行区域)。
如果主线程已经在
Console.ReadKey();
它不会“备份”到try/catch
块。