Cómo atrapar excepciones de un ThreadPool.QueueUserWorkItem?


Tengo el siguiente código que lanza una excepción:

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

Cuando la acción lanza una excepción, mi programa se bloquea. ¿Cuál es la mejor práctica para manejar esta situación?


Relacionado: Excepciones en los subprocesos. Net ThreadPool

Author: Community, 2009-04-16

5 answers

Si tiene acceso al código fuente de action, inserte un bloque try/catch en ese método; de lo contrario, cree un nuevo método tryAction que envuelva la llamada a action en un bloque try/catch.

 24
Author: Tormod Fjeldskår,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-15 21:41:50

Puedes añadir try / catch así:

        ThreadPool.QueueUserWorkItem(state =>
                                         {
                                             try
                                             {
                                                 action();
                                             }
                                             catch (Exception ex)
                                             {
                                                 OnException(ex);
                                             }
                                         });
 70
Author: Prankster,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-15 21:43:34

Si está utilizando.Net 4.0, podría valer la pena investigar la clase Task porque puede encargarse de esto por usted.

El equivalente de su código original, pero usando Tareas, se ve como

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

Para lidiar con las excepciones, puede agregar una continuación a la Tarea devuelta por StartNew. Podría verse así:

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
Author: Samuel Jack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-09-12 18:51:07

En el otro hilo, (en el método "colas", se agrega una cláusula try catch... .Luego, en catch, coloque la excepción captada en una variable de excepción compartida (visible para el hilo principal).

Luego, en su subproceso principal, cuando todos los elementos en cola hayan terminado (use una matriz de manejadores de espera para esto) Verifique si algún subproceso llenó esa excepción compartida con una excepción... Si lo hizo, repensarlo o manejarlo como corresponda...

Aquí hay un código de ejemplo de un proyecto reciente I usé esto para...
HasException es booleano compartido...

    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
Author: Charles Bretana,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-15 21:49:09

Lo que suelo hacer es crear un gran intento ... bloque catch dentro del método action() a continuación, almacenar la excepción como una variable privada y luego manejarla dentro del hilo principal

 1
Author: oscarkuo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-15 21:41:12