C# 如何从 ThreadPool.QueueUserWorkItem 中捕获异常?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/753841/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 23:51:54  来源:igfitidea点击:

How to catch exceptions from a ThreadPool.QueueUserWorkItem?

c#.netmultithreadingthreadpool

提问by Michael Hedgpeth

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

相关:.Net ThreadPool 线程异常

采纳答案by Tormod Fjeldsk?r

If you have access to action's source code, insert a try/catch block in that method; otherwise, create a new tryActionmethod which wraps the call to actionin a try/catch block.

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

回答by oscarkuo

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

我通常做的是在 action() 方法中创建一个大的 try ... catch 块,然后将异常存储为私有变量,然后在主线程中处理它

回答by Charles Bretana

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).

在另一个线程上,(在您“排队”的方法中,添加一个 try catch 子句...。然后在 catch 中,将捕获的异常放入共享的 Exception 变量中(对主线程可见)。

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...

这是我最近使用它的一个项目的一些示例代码......
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;
    }

回答by Prankster

You can add try/catch like this:

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

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

回答by Samuel Hyman

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

如果您使用 .Net 4.0,可能值得研究Task类,因为它可以为您解决这个问题。

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:

要处理异常,您可以向 StartNew 返回的 Task 添加延续。它可能看起来像这样:

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);

回答by SHO

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);
    }
}