Вопрос

Дубликат: Как перехватить исключения из 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, когда это происходит, но мне нужно «обработать» это исключение в методе, в котором эти дочерние потоки ставятся в очередь и создаются, чтобы контролировать немедленную последующую обработку на основе успеха дочернего процесса. тройки...Как мне это сделать?

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

Решение

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top