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?


Related: Exceptions on .Net ThreadPool Threads

44
demandé sur Community 2009-04-16 01:37:31

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.

24
répondu Tormod Fjeldskår 2009-04-15 21:41:50

vous pouvez ajouter try / catch comme ceci:

        ThreadPool.QueueUserWorkItem(state =>
                                         {
                                             try
                                             {
                                                 action();
                                             }
                                             catch (Exception ex)
                                             {
                                                 OnException(ex);
                                             }
                                         });
70
répondu Prankster 2009-04-15 21:43:34

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);
19
répondu Samuel Jack 2011-09-12 18:51:07

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;
    }
3
répondu Charles Bretana 2009-04-15 21:49:09

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

1
répondu oscarkuo 2009-04-15 21:41:12