Как отловить исключения из ThreadPool.QueueUserWorkItem?
У меня есть следующий код, который выдает исключение:
ThreadPool.QueueUserWorkItem(state => action());
Когда действие вызывает исключение, моя программа падает. Какова лучшая практика для решения этой ситуации?
Связанный: Исключения на.Net ThreadPool Threads
5 ответов
Если у вас есть доступ к action
исходный код, вставьте блок try/catch в этот метод; в противном случае создайте новый tryAction
метод, который оборачивает вызов action
в блоке try/catch.
Вы можете добавить попробовать / поймать, как это:
ThreadPool.QueueUserWorkItem(state =>
{
try
{
action();
}
catch (Exception ex)
{
OnException(ex);
}
});
Если вы используете.Net 4.0, возможно, стоит изучить класс Task, потому что он позаботится об этом за вас.
Эквивалент вашего исходного кода, но с использованием задач выглядит
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;
}
Обычно я создаю большой блок try ... 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);
}
}