Как перехватывать исключения из ThreadPool.QueueUserWorkItem?

StackOverflow https://stackoverflow.com/questions/753841

Вопрос

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

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

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


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

Это было полезно?

Решение

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

Другие советы

Вы можете добавить try/catch следующим образом:

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

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

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

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);

В другом потоке (в методе, который вы «ставите в очередь», добавьте предложение 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;
    }

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

Простой код:

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);
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top