Как перехватить исключения из ThreadPool.QueueUserWorkItem? - PullRequest
44 голосов
/ 16 апреля 2009

У меня есть следующий код, который выдает исключение:

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

Когда действие вызывает исключение, моя программа падает. Как лучше всего справляться с этой ситуацией?


Похожие: Исключения в потоках .Net ThreadPool

Ответы [ 6 ]

70 голосов
/ 16 апреля 2009

Вы можете добавить попробовать / поймать, как это:

        ThreadPool.QueueUserWorkItem(state =>
                                         {
                                             try
                                             {
                                                 action();
                                             }
                                             catch (Exception ex)
                                             {
                                                 OnException(ex);
                                             }
                                         });
24 голосов
/ 16 апреля 2009

Если у вас есть доступ к исходному коду action, вставьте блок try / catch в этот метод; в противном случае создайте новый метод tryAction, который обернет вызов action в блок try / catch.

19 голосов
/ 12 сентября 2011

Если вы используете .Net 4.0, возможно, стоит изучить класс Task , потому что он позаботится об этом за вас.

Эквивалент вашего исходного кода, но с использованием задач, выглядит как

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

Чтобы справиться с исключениями, вы можете добавить продолжение к Задаче, возвращаемой StartNew. Это может выглядеть так:

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);
3 голосов
/ 16 апреля 2009

В другом потоке (в методе, который вы "ставите в очередь", добавьте предложение try catch .... Затем в catch поместите перехваченное исключение в общую переменную Exception (видимую для основного потока).

Затем в вашем основном потоке, когда все элементы в очереди закончили (для этого используйте массив дескрипторов ожидания). Проверьте, не заполнен ли какой-либо поток этим общим исключением с исключением ... Если это так, перебросьте его или обработайте соответствующим образом. ..

вот пример кода из недавнего проекта, который я использовал для ...
HasException является общим логическим значением ...

    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;
    }
1 голос
/ 16 апреля 2009

Что я обычно делаю, так это создаю большой блок try ... catch внутри метода action () затем сохраните исключение как частную переменную, затем обработайте его внутри основного потока

0 голосов
/ 18 июня 2019

Простой код:

public class Test
{
    private AutoResetEvent _eventWaitThread = new AutoResetEvent(false);

    private void Job()
    {
        Action act = () =>
        {
            try
            {
                // do work...
            }
            finally
            {
                _eventWaitThread.Set();
            }
        };
        ThreadPool.QueueUserWorkItem(x => act());
        _eventWaitThread.WaitOne(10 * 1000 * 60);
    }
}
...