views:

2522

answers:

4

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?


Related: Exceptions on .Net ThreadPool Threads

+1  A: 

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

oykuo
+1  A: 

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;
    }
Charles Bretana
what is a "shared Exception variable (visible to the main thread)"? What if multiple exceptions are thrown?
MedicineMan
In sample code above, X is the shared Exception variable... Its just a variable declared at class level so it is "shared" among all methods in class... And As soon as the first exception is thrown, don't you kinda want to stop "?
Charles Bretana
+7  A: 

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.

Tormod Fjeldskår
+13  A: 

You can add try/catch like this:

        ThreadPool.QueueUserWorkItem(state =>
                                         {
                                             try
                                             {
                                                 action();
                                             }
                                             catch (Exception ex)
                                             {
                                                 OnException(ex);
                                             }
                                         });
Prankster
+1 for a cool use of a lambda. I found that if a worker thread in a thread pool threw an unhandled exception in my web app, it would crash the entire web server. This saved me! Thanks!
rally25rs