How to catch exceptions from a ThreadPool.QueueUserWorkItem?

后端 未结 6 1791
借酒劲吻你
借酒劲吻你 2020-12-01 07:19

I have the following code that throws an exception:

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

When the action throws an exception

相关标签:
6条回答
  • 2020-12-01 07:48

    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 讨论(0)
  • 2020-12-01 07:50

    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

    0 讨论(0)
  • 2020-12-01 07:55

    You can add try/catch like this:

            ThreadPool.QueueUserWorkItem(state =>
                                             {
                                                 try
                                                 {
                                                     action();
                                                 }
                                                 catch (Exception ex)
                                                 {
                                                     OnException(ex);
                                                 }
                                             });
    
    0 讨论(0)
  • 2020-12-01 07:59

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

    The equivalent of your original code, but using Tasks, looks like

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

    To deal with 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);
    
    0 讨论(0)
  • 2020-12-01 08:00

    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;
        }
    
    0 讨论(0)
  • 2020-12-01 08:10

    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.

    0 讨论(0)
提交回复
热议问题