Comment attraper les exceptions d'un ThreadPool.Queeusuerworkitem?
j'ai le code suivant qui lance une exception:
ThreadPool.QueueUserWorkItem(state => action());
quand l'action lance une exception, mon programme s'écroule. Quelle est la meilleure pratique pour gérer cette situation?
5 réponses
si vous avez accès au code source de action
, insérez un bloc try/catch dans cette méthode; sinon, créez une nouvelle méthode tryAction
qui enveloppe l'appel à action
dans un bloc try/catch.
vous pouvez ajouter try / catch comme ceci:
ThreadPool.QueueUserWorkItem(state =>
{
try
{
action();
}
catch (Exception ex)
{
OnException(ex);
}
});
si vous utilisez .Net 4.0, il pourrait être intéressant d'étudier la classe tâche parce qu'il peut prendre soin de cela pour vous.
l'équivalent de votre code d'origine, mais en utilisant des tâches, ressemble à
Task.Factory.StartNew(state => action(), state);
pour traiter les exceptions, vous pouvez ajouter une suite à la tâche retournée par StartNew. Il pourrait ressembler à ceci:
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);
sur l'autre fil, (dans la méthode vous faites la file d'attente, ajoutez une clause try catch... .Ensuite, dans la capture, placez l'exception capturée dans une variable D'Exception partagée (visible par le thread principal).
puis dans votre thread principal, lorsque tous les éléments mis en file d'attente sont terminés (utilisez un tableau d'attente pour cela), vérifiez si un thread a généré une exception partagée avec une exception... Si elle le faisait, renvoyer ou de le traiter comme approprié...
un exemple de code d'un projet récent pour lequel j'ai utilisé ça...
HasException est un booléen partagé...
private void CompleteAndQueuePayLoads(
IEnumerable<UsagePayload> payLoads, string processId)
{
List<WaitHandle> waitHndls = new List<WaitHandle>();
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;
}
Ce que je fais habituellement est de créer un grand essai ... bloc de capture dans la méthode action() puis stocker l'exception comme une variable privée puis la manipuler à l'intérieur du fil principal