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