4

重复: 如何从 ThreadPool.QueueUserWorkItem 中捕获异常?


我在 .Net ThreadPool 上为大量独立的远程调用排队多个委托,这些调用本身调用多个数据库和其他离线资源。通过在 ThreadPool 上排队这些调用,我可以同时运行它们并最小化整体延迟。

private void CompleteAndQueuePayLoads(IEnumerable<UsagePayload> payLoads)
{
    List<WaitHandle> waitHndls = new List<WaitHandle>();
    foreach (UsagePayload uPyLd in payLoads)
    {
        ManualResetEvent txEvnt = new ManualResetEvent(false);
        UsagePayload uPyLd1 = uPyLd ;
        ThreadPool.QueueUserWorkItem(
            delegate
                {
                    if (!uPyLd1 .IsComplete)
                        // IEEDAL.GetPayloadReadings is long running DB call
                        try { IEEDAL.GetPayloadReadings(uPyLd1 ); }
                        catch (IEEAccessException iX)
                        {
                            log.Write(log.Level.Error,
                                  "IEEWSDAL.CompleteAndQueuePayLoads " + 
                                   " Delegate Failed " +
                                  iX.Message, iX);
                            txEvnt.Set();
                            throw;  // this causes parent thread to crash!
                            // was going to try Thread.Abort next ...
                            // Thread.CurrentThread.Abort();
                        }
                    UsageCache.PersistPayload(uPyLd1 );
                    SavePayLoadToProcessQueueFolder(uPyLd1 );
                    txEvnt.Set();
                });
        waitHndls.Add(txEvnt);
    }
    util.WaitAll(waitHndls.ToArray()); //To waitone on > 64 waithandles
}

但是整个批处理需要事务处理,IE,只有在所有子线程都成功的情况下,才需要允许父线程的输出继续进行。我已经将子线程编码为在失败时引发自定义异常,但我发现这会导致父线程崩溃,因为这些异常不能在父线程中“捕获”......

我已经阅读了发生这种情况时 CLR 抛出的 UnHandledExceptionEvent 的信息,但是我需要在这些子线程排队和产生的方法中“处理”这个异常,以根据孩子的成功控制立即的下游处理threeads... 我该怎么做呢?

4

3 回答 3

3

如果线程失败,您可以在 CompleteAndQueuePayLoads 函数的局部变量中标记至少发生一次失败,并添加异常/失败变量以供稍后检查。

于 2009-03-20T22:58:02.567 回答
2

我在当前的项目中经常这样做。我采用的方法是制作自己的调度程序来接收委托,将其添加到我自己的队列中,然后运行它们以在最后处理同步等。

我在这种情况下使用的“技巧”是不要直接在线程池上调用委托,而是在我的调度程序中调用一个方法来包装委托并更优雅地处理异常。这样,实际的内部方法调用总是以适当的(对我而言)方式处理。

当异常发生时,我的调度程序会收到通知并停止调度未来的任务(这是一个很好的奖励),但也会告诉调用者有一个异常。

于 2009-03-21T01:04:29.470 回答
1

您可能会查看 3.5 的并行库扩展。您可以在代码块上使用 Parallel.Invoke 来完成您正在尝试的操作。

于 2009-10-29T05:31:55.537 回答