Question

I'm trying to recreate the conditions that will cause this 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.`

I wrote this program thinking I'd cause the exception but it does not:

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

In my real application, I use TPL and I did not code my exception handling right. As a result I get that exception. Now I'm trying to recreate the same conditions in a separate program to experiment with unobserved exceptions.

Was it helpful?

Solution 5

I'm the OP. I tested the GC.WaitForPendingFinalizers() but it did not help recreating the exception. The problem was that the GC.Collect() was executed before the task started.

This is the correct code to recreate the exception:

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

OTHER TIPS

You may need to add a call to GC.WaitForPendingFinalizers() after GC.Collect() since finalizers run on their own thread.

The exception is thrown by TaskExceptionHolder's finalizer, so the finalizer thread must run before this exception is thrown. As Josh points out, you can wait for that to happen by calling CG.WaitForPedingFinalizers().

Please notice that this behavior has been changed in the current Async CTP. I spoke to Stephen Toub of the PFX team about this at TechEd Europe earlier this year, and he indicated that they had to change it for the new async functionality to work correctly. So while it is still too early to say anything about the next version of the framework, this behavior could very well be changed in the upcoming version.

@Sly, although you came up with a working answer, I think most folks would be better served by heeding the error message suggestion "... Waiting on the Task ...". Getting involved in GC activity is a sign that either you know GC intimately and have a performance bottleneck or you are missing the point. In my case, it means the latter ;) The StartNew call does return a Task so why not use it? e.g.

Task myTask = Task.Factory.StartNew(() => { throw new NullReferenceException("ex"); }); // give some time to the task to complete
myTask.Wait();

I'm really surprised that you didn't attempt to properly call code after task completion because who's to say that any process will complete under 3 seconds? Not only that but that ties up other processes for a full 3 seconds. I would replace that implementation with the ContinueWith() task method to call the GC after the task completes.

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

If you need block until it's complete (for your example code you were using to debug), you could also do a WaitOne after starting the task and have ContinueWith() signal the wait handler. If you're having to do this in your production code then maybe what you're trying to accomplish is actually synchronous where you don't need to worry about using a task at all.

The simplest way to recreate the error is waiting for the task to complete.

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

Calling wait will block the call until the task has finished execution.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top