如何使用TPL导致AggregateException?

时间:2010-12-22 05:40:24

标签: c# task-parallel-library unobserved-exception

我正在尝试重新创建导致此异常的条件:

System.AggregateException: A Task's exception(s) were not observed 
either by Waiting on the Task or accessing its Exception property. 
As a result, the unobserved exception was rethrown by the finalizer thread.`

我写了这个程序,认为我会引起异常,但事实并非如此:

using System;
using System.Threading.Tasks;
namespace SomeAsyncStuff
{
    class Program
    {
        static void Main(string[] args)
        {
            Task.Factory.StartNew(() => { throw new NullReferenceException("ex"); });
            GC.Collect();
            Console.WriteLine("completed");            
        }
    }
}

在我的实际应用程序中,我使用TPL并且我没有编写正确的异常处理代码。结果我得到了那个例外。现在,我正在尝试在单独的程序中重新创建相同的条件,以试验未观察到的异常。

6 个答案:

答案 0 :(得分:4)

您可能需要在GC.Collect()之后添加对GC.WaitForPendingFinalizers()的调用,因为终结器在他们自己的线程上运行。

答案 1 :(得分:3)

TaskExceptionHolder的终结器抛出异常,因此终结器线程必须在抛出此异常之前运行。正如乔希指出的那样,你可以通过拨打CG.WaitForPedingFinalizers()来等待。

请注意,当前的异步CTP中已更改此行为。我在今年早些时候在TechEd Europe上与PFX团队的Stephen Toub谈过这个问题,他表示他们必须改变它以使新的异步功能正常工作。因此,尽管对框架的下一个版本说什么还为时尚早,但在即将发布的版本中,这种行为很可能会发生变化。

答案 2 :(得分:1)

@Sly,虽然你提出了一个有效的答案,但我认为通过听取错误消息建议“......等待任务......”可以更好地服务大多数人。参与GC活动是一个迹象,表明您非常了解GC并且存在性能瓶颈,或者您忽略了这一点。在我的情况下,它意味着后者;)StartNew调用确实返回一个任务,所以为什么不使用它? e.g。

任务myTask = Task.Factory.StartNew(()=>         {             抛出新的NullReferenceException(“ex”);         }); //给一些时间完成任务         myTask.Wait();

答案 3 :(得分:0)

我很惊讶您在任务完成后没有尝试正确调用代码,因为谁说任何进程将在3秒内完成?不仅如此,它还会占用其他进程3秒钟。我将使用ContinueWith()任务方法替换该实现,以在任务完成后调用GC。

Task.Factory
    .StartNew(() => { throw new NullReferenceException("ex"); })
    .ContinueWith(p => GC.Collect());

如果需要阻塞直到它完成(对于您用于调试的示例代码),您还可以在启动任务后执行WaitOne并让ContinueWith()发出等待处理程序的信号。如果您必须在生产代码中执行此操作,那么您尝试完成的任务实际上是同步的,您根本不需要担心使用任务。

答案 4 :(得分:-1)

我是OP。我测试了GC.WaitForPendingFinalizers(),但它没有帮助重新创建异常。问题是在任务开始之前执行了GC.Collect()。

这是重新创建例外的正确代码:

using System;
using System.Threading;
using System.Threading.Tasks;
namespace SomeAsyncStuff
{
    class Program
    {
        static void Main(string[] args)
        {
            Task.Factory.StartNew(() => { throw new NullReferenceException("ex"); });

            // give some time to the task to complete
            Thread.Sleep(3000);

            GC.Collect();
            // GC.WaitForPendingFinalizers();
            Console.WriteLine("completed"); 
        }
    }
}

答案 5 :(得分:-1)

重新创建错误的最简单方法是等待任务完成。

Task task = Task.Factory.StartNew(() => { throw new NullReferenceException("ex"); });
//this is where the exception will be thrown
task.Wait();

调用wait将阻止调用,直到任务完成执行。