如何从ThreadPool.QueueUserWorkItem中捕获exception?

我有下面的代码引发exception:

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

当动作引发exception时,我的程序崩溃。 处理这种情况的最佳做法是什么?


相关: .Net ThreadPool线程的例外

如果您有权访问action的源代码,请在该方法中插入try / catch块; 否则,创build一个新的tryAction方法,将调用的action包装在try / catch块中。

你可以像这样添加try / catch:

  ThreadPool.QueueUserWorkItem(state => { try { action(); } catch (Exception ex) { OnException(ex); } }); 

如果您使用的是.Net 4.0,则可能需要调查Task类,因为它可以为您处理此问题。

你原来的代码相当于使用任务,看起来像

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

要处理exception,可以将继续添加到由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子句…。然后在捕获中,将捕获的exception放入一个共享的Exceptionvariables(对主线程可见)。

然后在你的主线程,当所有排队的项目已经完成(使用一个等待句柄数组为这个)检查是否有一个线程填充共享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; } 

我通常做的是在action()方法中创build一个大的try … catch块,然后将该exception存储为一个私有variables,然后在主线程中处理它