Исключения в потоках .Net ThreadPool
-
21-08-2019 - |
Вопрос
Дубликат: Как перехватить исключения из 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 в своем блоке кода, чтобы сделать именно то, что вы пытаетесь.