2010-10-19 28 views
6

Sulla base della mia ricerca, ho imparato quanto segue:TaskScheduler.UnobservedTaskException non viene mai chiamato

  1. TaskScheduler.UnobservedTaskException deve attendere che il compito da garbage collection prima eccezione inosservato quel compito sarà bolla fino alla manifestazione UnobservedTaskException.
  2. Se si utilizza Task.Wait(), non verrà mai richiamato comunque, perché si sta bloccando un risultato imminente dall'attività, quindi l'eccezione verrà generata su Task.Wait() invece che bolla fino all'evento UnobservedException.
  3. Chiamare il numero GC.Collect() manualmente è in genere una cattiva idea, a meno che non si sappia esattamente cosa si sta facendo, quindi è utile in questo caso per confermare le cose, ma non come una soluzione adeguata al problema.

Il problema

Se le mie uscite di applicazione prima che il garbage collector entra in gioco, sono assolutamente al 100% non possono ottenere il mio caso UnobservedTaskException al fuoco.

Nota il seguente codice:

class Program 
{ 
    static void Main(string[] args) 
    { 
     TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; 
     Task.Factory.StartNew(() => 
     { 
      Console.WriteLine("Task started."); 
      throw new Exception("Test Exception"); 
     }); 
     Thread.Sleep(1000); 
     //GC.Collect(); 
     //GC.WaitForPendingFinalizers(); 
    } 

    static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) 
    { 
     File.WriteAllText(@"C:\data\TestException.txt", e.Exception.ToString()); 
     Console.WriteLine("EXCEPTION UNOBSERVED"); 
    } 
} 

Nessun eccezione è scritto e nulla è scritto nella console. 10-15 minuti e più possono passare dopo che l'applicazione è terminata e ancora non vedo alcuna prova che i resti della mia applicazione siano stati raccolti. Potresti chiedere, beh, perché non raccoglierti solo all'uscita? Bene, il mio scenario del mondo reale è che il mio trapping di eccezione viene eseguito all'interno di un servizio WCF ospitato all'interno di un servizio di Windows, ovvero . Non riesco a intercettarlo quando il servizio di Windows si sta spegnendo (e quindi manualmente chiama GC.Collect()) perché non c'è nessun evento per quello che posso vedere.

Dove sto andando male? Come faccio a garantire che se qualcosa in profondità all'interno del servizio WCF interrompe in definitiva il servizio Windows, ho la possibilità di registrare l'eccezione prima che il servizio cada?

risposta

2

Nathan,

I punti sono tutti veri. Provare quanto segue:

namespace ConsoleApplication1 
{ 
    using System; 
    using System.Threading; 
    using System.Threading.Tasks; 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; 
      Task.Factory.StartNew(() => 
      { 
       Console.WriteLine("Task started."); 
       throw new Exception("Test Exception"); 
      }); 

      Thread.Sleep(1000); 
      Console.WriteLine("First Collect"); 
      GC.Collect(); 
      GC.WaitForPendingFinalizers(); 
      Console.WriteLine("Waiting"); 
      Console.ReadKey(); 
     } 

     static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) 
     { 
      Console.WriteLine("EXCEPTION UNOBSERVED"); 
     } 
    } 
} 

ho notato che il debugger spesso "trappole" della manifestazione UnobservedTaskException, inducendolo a non sparare in modo appropriato. Esegui questo al di fuori del debugger, e stamperà "ECCEZIONE UNOBSERVED" ogni volta prima di spegnersi.

+0

Ciao, grazie per il suggerimento, anche se in realtà lo sto già eseguendo all'esterno del debugger.Noto anche che stai usando GC.Collect(). Cosa succede nel tuo esempio se non raccogli manualmente? –

+0

@Nathan: Normalmente, non c'è abbastanza pressione GC per fare in modo che ciò accada in questo semplice esempio. Se il GC non si accumula, il "task" non colpirà il finalizzatore e l'UnobservedException non si attiva mai. Succede solo quando il finalizzatore raccoglie un'attività senza root con un'eccezione. –

+0

@Nathan: si noti che UnobservedTaskException non impedirà l'arresto della app in ogni caso: è necessario gestire le eccezioni all'interno dell'attività o attendere sempre un'attività, se si desidera impedire l'arresto di un'app. –

5

Per me, TaskScheduler.UnobServedTaskException all'inizio fornisce un senso di sicurezza molto errato. Non vale davvero molto la pena se dipende da Garbage Collection.

Ho trovato la seguente soluzione, presa da this msdn article, molto più affidabile. Esegue fondamentalmente il blocco di continuazione (in cui si registra l'eccezione) solo se sono presenti eccezioni non gestite in task1 e non blocca l'esecuzione dell'interfaccia utente.

Si potrebbe anche voler appiattire nidificato AggregateExceptions e forse creare un metodo di estensione, come Reed Copsey depicted here.

var task1 = Task.Factory.StartNew(() => 
{ 
    throw new MyCustomException("Task1 faulted."); 
}) 
.ContinueWith((t) => 
{ 
    Console.WriteLine("I have observed a {0}", 
     t.Exception.InnerException.GetType().Name); 
}, 
TaskContinuationOptions.OnlyOnFaulted); 
Problemi correlati