C# 如何使用TPL引起聚合异常?

C# 如何使用TPL引起聚合异常?,c#,task-parallel-library,unobserved-exception,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; usi

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

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;
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"); 
        }
    }
}
我编写此程序时认为我会导致异常,但它不会:

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

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

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

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


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

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


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

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

这是重新创建异常的正确代码:

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;
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"); 
        }
    }
}

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

这是重新创建异常的正确代码:

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;
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"); 
        }
    }
}

@Sly,虽然你给出了一个有效的答案,但我认为大多数人最好注意错误消息建议“…等待任务…”。参与GC活动是一种迹象,表明要么您非常了解GC,存在性能瓶颈,要么您没有抓住重点。就我而言,它指的是后者;)StartNew调用确实返回任务,为什么不使用它呢? e、 g

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

myTask.Wait()

@Sly,虽然你给出了一个有效的答案,但我认为大多数人最好注意错误消息建议“…等待任务…”。参与GC活动是一种迹象,表明要么您非常了解GC,存在性能瓶颈,要么您没有抓住重点。就我而言,它指的是后者;)StartNew调用确实返回任务,为什么不使用它呢? e、 g

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

myTask.Wait()

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

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

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

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

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

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

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

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

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

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

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

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

我试图重新创建AggregateException,以便试验程序以“触发并忘记”方式执行任务时可能发生的未观察到的异常。您的方法模拟一个程序,该程序以“激发并等待结果”的方式执行任务。我认为调用GC.Collect()可以更好地模拟这样一种情况,即当GC启动并处理TaskExceptionHolder时,程序会感到“惊讶”