Исключения в потоках.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 в своем блоке кода, чтобы делать именно то, что вы пытаетесь.