Come catturare le eccezioni da un object ThreadPool.QueueUserWorkItem?

Ho il seguente codice che genera un’eccezione:

ThreadPool.QueueUserWorkItem(state => action()); 

Quando l’azione genera un’eccezione, il mio programma si arresta in modo anomalo. Qual è la migliore pratica per gestire questa situazione?


Correlato: eccezioni sui thread ThreadNet di Net

Se si ha accesso al codice sorgente action , inserire un blocco try / catch in tale metodo; altrimenti, crea un nuovo metodo tryAction che avvolge l’invito action in un blocco try / catch.

Puoi aggiungere try / catch in questo modo:

  ThreadPool.QueueUserWorkItem(state => { try { action(); } catch (Exception ex) { OnException(ex); } }); 

Se stai usando .Net 4.0, potrebbe valere la pena di investigare sulla class Task perché ti può prendere cura di te.

L’equivalente del tuo codice originale, ma usando Task, assomiglia

 Task.Factory.StartNew(state => action(), state); 

Per gestire le eccezioni è ansible aggiungere una continuazione all’attività restituita da StartNew. Potrebbe assomigliare a questo:

 var task = Task.Factory.StartNew(state => action(), state); task.ContinueWith(t => { var exception = t.Exception.InnerException; // handle the exception here // (note that we access InnerException, because tasks always wrap // exceptions in an AggregateException) }, TaskContinuationOptions.OnlyOnFaulted); 

Sull’altro thread, (nel metodo si sta “accodando”, aggiungi una clausola try try … .Quando nel catch, posiziona l’eccezione catturata in una variabile Exception condivisa (visibile al thread principale).

Poi nel tuo thread principale, quando tutti gli elementi in coda sono finiti (usa una matrice di handle di attesa per questo) Controlla se qualche thread ha popolato quell’eccezione condivisa con un’eccezione … Se lo facesse, ricollegalo o gestirlo come appropriato …

ecco alcuni esempi di codice da un progetto recente che ho usato per …
HasException è condiviso booleano …

  private void CompleteAndQueuePayLoads( IEnumerable payLoads, string processId) { List waitHndls = new List(); int defaultMaxwrkrThreads, defaultmaxIOThreads; ThreadPool.GetMaxThreads(out defaultMaxwrkrThreads, out defaultmaxIOThreads); ThreadPool.SetMaxThreads( MDMImportConfig.MAXCONCURRENTIEEUSAGEREQUESTS, defaultmaxIOThreads); int qryNo = 0; foreach (UsagePayload uPL in payLoads) { ManualResetEvent txEvnt = new ManualResetEvent(false); UsagePayload uPL1 = uPL; int qryNo1 = ++qryNo; ThreadPool.QueueUserWorkItem( delegate { try { Thread.CurrentThread.Name = processId + "." + qryNo1; if (!HasException && !uPL1.IsComplete) IEEDAL.GetPayloadReadings(uPL1, processId, qryNo1); if (!HasException) UsageCache.PersistPayload(uPL1); if (!HasException) SavePayLoadToProcessQueueFolder( uPL1, processId, qryNo1); } catch (MeterUsageImportException iX) { log.Write(log.Level.Error, "Delegate failed " iX.Message, iX); lock (locker) { HasException = true; X = iX; foreach (ManualResetEvent txEvt in waitHndls) txEvt.Set(); } } finally { lock(locker) txEvnt.Set(); } }); waitHndls.Add(txEvnt); } util.WaitAll(waitHndls.ToArray()); ThreadPool.SetMaxThreads(defaultMaxwrkrThreads, defaultmaxIOThreads); lock (locker) if (X != null) throw X; } 

Quello che faccio di solito è creare un grande tentativo … catturare il blocco all’interno del metodo action () quindi archiviare l’eccezione come variabile privata e gestirla all’interno del thread principale