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时,程序会感到“惊讶”