Domanda

Sto cercando di ricreare le condizioni che causano questa eccezione:

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.`

Ho scritto questo programma pensando che avrei causo l'eccezione, ma non:

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

Nella mia vera domanda, io uso TPL e non mi ha codificato la mia gestione delle eccezioni a destra. Come risultato ottengo questa eccezione. Ora sto cercando di ricreare le stesse condizioni in un programma separato per sperimentare con eccezioni inosservate.

È stato utile?

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

Altri suggerimenti

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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top