I have the following code that throws an exception:
ThreadPool.QueueUserWorkItem(state => action());
When the action throws an exception, my program crashes. What is the best practice for handling this situation?
I have the following code that throws an exception:
ThreadPool.QueueUserWorkItem(state => action());
When the action throws an exception, my program crashes. What is the best practice for handling this situation?
What I usually do is to create a big try ... catch block inside the action() method then store the exception as a private variable then handle it inside the main thread
On the other thread, (in the method you are "queueing" up, add a try catch clause... .Then in the catch, place the caught exception into a shared Exception variable (visible to the main thread).
Then in your main thread, when all queued items have finished (use a wait handle array for this) Check if some thread populated that shared exception with an exception... If it did, rethrow it or handle it as appropriate...
here's some sample code from a recent project I used this for...
HasException is shared boolean...
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;
}
If you have access to action
's source code, insert a try/catch block in that method; otherwise, create a new tryAction
method which wraps the call to action
in a try/catch block.
You can add try/catch like this:
ThreadPool.QueueUserWorkItem(state =>
{
try
{
action();
}
catch (Exception ex)
{
OnException(ex);
}
});