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

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


Я ставлю в очередь несколько делегатов в.Net ThreadPool для большого количества независимых удаленных вызовов, которые сами вызывают несколько баз данных и других автономных ресурсов. Очередь этих вызовов в ThreadPool позволяет выполнять их одновременно и минимизировать общую задержку.

private void CompleteAndQueuePayLoads(IEnumerable<UsagePayload> payLoads)
{
    List<WaitHandle> waitHndls = new List<WaitHandle>();
    foreach (UsagePayload uPyLd in payLoads)
    {
        ManualResetEvent txEvnt = new ManualResetEvent(false);
        UsagePayload uPyLd1 = uPyLd ;
        ThreadPool.QueueUserWorkItem(
            delegate
                {
                    if (!uPyLd1 .IsComplete)
                        // IEEDAL.GetPayloadReadings is long running DB call
                        try { IEEDAL.GetPayloadReadings(uPyLd1 ); }
                        catch (IEEAccessException iX)
                        {
                            log.Write(log.Level.Error,
                                  "IEEWSDAL.CompleteAndQueuePayLoads " + 
                                   " Delegate Failed " +
                                  iX.Message, iX);
                            txEvnt.Set();
                            throw;  // this causes parent thread to crash!
                            // was going to try Thread.Abort next ...
                            // Thread.CurrentThread.Abort();
                        }
                    UsageCache.PersistPayload(uPyLd1 );
                    SavePayLoadToProcessQueueFolder(uPyLd1 );
                    txEvnt.Set();
                });
        waitHndls.Add(txEvnt);
    }
    util.WaitAll(waitHndls.ToArray()); //To waitone on > 64 waithandles
}

Но весь пакет должен обрабатываться транзакционно, т.е. вывод родительского потока должен выполняться только в том случае, если все дочерние потоки были успешными. Я закодировал дочерний поток, чтобы он выдавал пользовательское исключение, когда оно не удавалось, но я обнаружил, что это вызывает сбой родительского потока, так как эти исключения не могут быть "перехвачены" в родительском потоке...

Когда это произошло, я читал о выбросе UnHandledExceptionEvent от CLR, но мне нужно "обработать" это исключение в методе, где эти дочерние потоки помещаются в очередь и порождаются, чтобы контролировать немедленную последующую обработку, основанную на успехе дочернего процесса. триады... Как мне это сделать?

3 ответа

Решение

Я делаю это много в моем текущем проекте. Подход, который я выбрал, состоял в том, чтобы создать свой собственный планировщик, который будет принимать делегата, добавлять его в мою очередь и запускать их для обработки синхронизации в конце и т. Д.

"Уловка", которую я использовал в этом случае, заключается в том, чтобы не вызывать делегат непосредственно в пуле потоков, а скорее вызывать метод в моем планировщике, который обертывает делегат и обрабатывает исключение более изящно. Таким образом, фактический внутренний вызов метода всегда обрабатывается соответствующим (для меня) способом.

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

Если поток завершается с ошибкой, вы можете отметить, что по крайней мере один сбой произошел внутри локальной переменной функции CompleteAndQueuePayLoads, и добавить переменную исключений / сбоев для последующего изучения.

Вы можете посмотреть на расширения библиотеки Parallel для 3.5. Вы можете использовать Parallel.Invoke в своем блоке кода, чтобы делать именно то, что вы пытаетесь.

Другие вопросы по тегам