How to catch exceptions from ThreadPool.QueueUserWorkItem?

I have the following code that throws an exception:

ThreadPool.QueueUserWorkItem(state => action()); 

When an action throws an exception, my program crashes. What is the best practice to handle this situation?




Related: Exceptions to .Net ThreadPool Threads

+44
multithreading c # threadpool
Apr 15 '09 at 21:37
source share
6 answers

If you have access to the source action , add a try / catch block to this method; otherwise, create a new tryAction method that completes the action call in the try / catch block.

+24
Apr 15 '09 at 21:41
source share

You can add try / catch as follows:

  ThreadPool.QueueUserWorkItem(state => { try { action(); } catch (Exception ex) { OnException(ex); } }); 
+70
Apr 15 '09 at 21:43
source share

If you are using .Net 4.0, it might be worth exploring the Task class because it can take care of this for you.

Equivalent to source code, but using Tasks, looks like

 Task.Factory.StartNew(state => action(), state); 

To handle exceptions, you can add a continuation to the Task returned by StartNew. It might look like this:

 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); 
+19
Sep 12 '11 at 18:51
source share

In another thread (in the method you β€œqueue”, add a try catch clause .... Then in catch put the thrown exception into the general Exception variable (visible in the main thread).

Then in your main thread when all the queued items are finished (use an array of wait descriptors for this). Check if a thread threw this general exception with an exception ... If so, flip it or handle it accordingly ...

here is a sample code from a recent project that I used for ... HasException is a common 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; } 
+3
Apr 15 '09 at 21:41
source share

What I usually do is make a big attempt ... catch block inside the action () method then save the exception as a private variable and then handle it inside the main thread

+1
Apr 15 '09 at 21:41
source share

Simple code:

 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); } } 
0
Jun 18 '19 at 5:20
source share



All Articles