0

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

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,但我没有正确编写异常处理代码。结果我得到了那个例外。现在我试图在一个单独的程序中重新创建相同的条件来试验未观察到的异常。

4

6 回答 6

4

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

于 2010-12-22T05:57:49.967 回答
3

异常是由TaskExceptionHolder的终结器引发的,因此终结器线程必须在引发此异常之前运行。正如 Josh 指出的那样,您可以通过调用CG.WaitForPedingFinalizers().

请注意,此行为在当前 Async CTP 中已更改。今年早些时候,我在 TechEd Europe 上与 PFX 团队的 Stephen Toub 进行了交谈,他表示他们必须更改它才能使新的异步功能正常工作。因此,虽然现在就框架的下一个版本说任何话还为时过早,但这种行为很可能在即将到来的版本中得到改变。

于 2010-12-22T10:22:41.863 回答
1

@Sly,尽管您想出了一个可行的答案,但我认为大多数人最好听听错误消息建议“...等待任务...”。参与 GC 活动表明您对 GC 非常了解并且遇到了性能瓶颈,或者您没有抓住重点。在我的情况下,这意味着后者;) StartNew 调用确实返回了一个 Task 那么为什么不使用它呢?例如

任务 myTask = Task.Factory.StartNew(() => { throw new NullReferenceException("ex"); }); // 给任务一些时间来完成
myTask.Wait();

于 2011-01-13T21:19:59.137 回答
0

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

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

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

于 2011-11-01T18:00:00.063 回答
-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"); 
        }
    }
}
于 2011-01-10T20:56:27.470 回答
-1

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

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

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

于 2012-03-28T06:23:21.630 回答