2013-07-26 8 views
5

Ho un thread di lavoro che, quando termina, segnala un evento. Questo evento viene quindi eseguito il marshalling sul thread principale per notificarlo della terminazione del thread di lavoro. Quando il thread di lavoro incontra un'eccezione non gestita, voglio che questa eccezione venga gestita dal sistema di gestione degli errori del thread principale. Pertanto, il thread worker imposta una proprietà che indica la sua chiusura imprevista e salva l'eccezione in un'altra proprietà, quindi segnala l'evento ed esce.Quale eccezione da generare quando il thread si chiude in modo imprevisto?

Dopo che l'evento è stato eseguito il marshalling sul thread principale, voglio lanciare una nuova eccezione con l'eccezione originale impostata come eccezione interna. La mia domanda è: quale dovrebbe essere il tipo di questa nuova eccezione? Esiste una specifica System.somethingException per questo tipo di situazione, dovrei progettare la mia classe Exception per questa specifica situazione o lanciare un System.Exception standard con un messaggio appropriato da considerare appropriato?

codice C# -psuedo:

class MyThread 
{ 
    public TerminationState Termination { get; private set; } 
    public Exception UncaughtException { get; private set; } 

    public delegate void ThreadTerminatedDelegate(MyThread thread); 
    public event ThreadTerminatedDelegate ThreadTerminated; 

    private void run() 
    { 
     try 
     { 
      doSomeWork(); 
     } 
     catch(Exception e) 
     { 
      UncaughtException = e; 
      Termination = TerminationState.AbortOnException; 
      ThreadTerminated(this); 
      return; 
     } 
     Termination = TerminationState.NormalTermination; 
     ThreadTerminated(this); 
    } 
} 

class MainThread 
{ 
    private MyThread myThread = new MyThread(); 

    private void run() 
    { 
     myThread.ThreadTerminated += handleTermination; 
     myThread.Start(); 
    } 

    private void handleTermination(MyThread thread) 
    { 
     if (InvokeRequired) 
     { 
      MyThread.ThreadTerminatedDelegate cb = new MyThread.ThreadTerminatedDelegate(handleTermination); 
      BeginInvoke(cb, new object[] { thread }); 
     } 
     else 
     { 
      if (thread.Termination == TerminationState.AbortOnException) 
      { 
       if (isFatal(thread.UncaughtException)) 
        throw new Exception("", thread.UncaughtException); //what to do here? 
       else 
        fixTheProblem(); 
      } 
      else 
      { 
       //normal wrapping up 
      } 
     } 
    } 
} 
+0

Direi che dipende da cosa sta facendo il thread.In realtà non mi piace AggregateException che appiattisce tutto su un errore generico così da ingoiare l'eccezione originale per qualcos'altro ... non sembra una buona idea. Lo clonarei (per mantenere la traccia dello stack originale) e lo avrei ripensato. Ma è solo la mia opinione ... –

+0

Che cosa esattamente speri di succedere quando rileggi l'eccezione? Le probabilità del 99% sono che il tuo programma si bloccherà. Che è una buona cosa, qualcosa non è stato fatto e non hai alcuna speranza di farlo. Ma poi, non preoccuparti di cogliere l'eccezione in primo luogo. Crollerà anche, ma almeno avrai una diagnostica migliore e non dovrai scrivere codice inutile. –

+0

@Adriano Invece di clonare e ripensare, non potresti semplicemente usare 'throw;'? Ciò mantiene la traccia dello stack originale, giusto? – JSQuareD

risposta

1

Io credo che è possibile eseguire tutte le eccezioni necessarie per carrelli elevatori in fondo non gestite eccezioni eseguendo il lavoro di fondo in una Task e quindi la gestione delle eccezioni in una continuazione di quel compito che è pianificato in modo esplicito per l'esecuzione sul thread principale. Ci sono opzioni aggiuntive che puoi specificare per la continuazione, ma questo dovrebbe riguardare il tuo scenario.

Task.Factory.StartNew(
    () => 
    { 
     // Do some work that may throw. 
     // This code runs on the Threadpool. 
     // Any exceptions will be propagated 
     // to continuation tasks and awaiters 
     // for observation. 
     throw new StackOverflowException(); // :) 
    } 
).ContinueWith(
    (a) => 
    { 
     // Handle your exception here. 
     // This code runs on the thread 
     // that started the worker task. 
     if (a.Exception != null) 
     { 
      foreach (var ex in a.Exception.InnerExceptions) 
      { 
       // Try to handle or throw. 
      } 
     } 
    }, 
    CancellationToken.None, 
    TaskContinuationOptions.None, 
    TaskScheduler.FromCurrentSynchronizationContext() 
); 

Un altro collegamento utile è MSDN's Asyncronous Programming Patterns. Identifica i 3 modi principali per implementare operazioni asincrone in un'applicazione. L'implementazione corrente sembra molto simile a ciò che l'articolo chiama EAP (Pattern asincrono basato su eventi).

Personalmente preferisco TAP (Task Asynchronous Pattern) basato su .NET 4.0 TPL (Task Parallel Library). Vale la pena imparare a causa della semplicità della sua sintassi e delle sue ampie capacità.

Da MSDN:

  • Asynchronous Programming Model (APM) modello (chiamato anche il modello IAsyncResult), dove le operazioni asincrone richiedono Begin e metodi finali (per esempio, BeginWrite e EndWrite per le operazioni di scrittura asincrone). Questo modello non è più raccomandato per il nuovo sviluppo. Per ulteriori informazioni, vedere Modello di programmazione asincrono (APM).
  • EAP (Asynchronous Pattern) basato sugli eventi, che richiede un metodo con il suffisso Async e richiede anche uno o più eventi, tipi di deleghe del gestore di eventi e tipi derivati ​​da EventArg. EAP è stato introdotto in .NET Framework 2.0. Non è più raccomandato per nuovi sviluppi. Per ulteriori informazioni, vedere Pattern asincrono basato sugli eventi (EAP).
  • Pattern asincrono basato su attività (TAP), che utilizza un singolo metodo per rappresentare l'avvio e il completamento di un'operazione asincrona. TAP è stato introdotto in .NET Framework 4 ed è l'approccio consigliato alla programmazione asincrona in .NET Framework. Per ulteriori informazioni, vedere Pattern asincrono basato su attività (TAP).

Inoltre, non dimenticare la fidata classe BackgroundWorker. Questa lezione è stata per me una costante per molto tempo e sebbene sia diventata un po 'deprecata da TAP, continuerà a svolgere il lavoro ed è molto facile da capire e da usare.

// Create a new background worker. 
var bgw = new BackgroundWorker(); 

// Assign a delegate to perform the background work. 
bgw.DoWork += (s, e) => 
    { 
     // Runs in background thread. Unhandled exceptions 
     // will cause the thread to terminate immediately. 
     throw new StackOverflowException(); 
    }; 

// Assign a delegate to perform any cleanup/error handling/UI updating. 
bgw.RunWorkerCompleted += (s, e) => 
    { 
     // Runs in UI thread. Any unhandled exception that 
     // occur in the background thread will be accessible 
     // in the event arguments Error property. 
     if (e.Error != null) 
     { 
      // Handle or rethrow. 
     } 
    }; 

// Start the background worker asynchronously. 
bgw.RunWorkerAsync(); 
+0

Grazie per la risposta! In questo momento sono in vacanza quindi non ho il tempo di guardarlo. Quando tornerò la prossima settimana, lo esaminerò in dettaglio e accetterò la risposta se risolverà il problema. :) – JSQuareD

+1

Sì, questa è l'alternativa elegante al mio brutto trucco, grazie! Inoltre, grazie per la supervisione di diversi schemi di programmazione asincrona, ho iniziato a leggere "C# in poche parole", e probabilmente riuscirò a risolverli nel prossimo futuro. :) – JSQuareD

Problemi correlati