C# 如何从ThreadPool.QueueUserWorkItem捕获异常?
我有以下引发异常的代码:C# 如何从ThreadPool.QueueUserWorkItem捕获异常?,c#,.net,multithreading,threadpool,C#,.net,Multithreading,Threadpool,我有以下引发异常的代码: ThreadPool.QueueUserWorkItem(state => action()); 当操作抛出异常时,我的程序崩溃。处理这种情况的最佳做法是什么 相关:我通常做的是创建一个大的尝试。。。action()方法中的catch块 然后将异常存储为一个私有变量,然后在另一个线程的主线程内处理它(在“排队”的方法中,添加一个try-catch子句……然后在catch中,将捕获的异常放入一个共享异常变量(主线程可见) 然后,在主线程中,当所有排队的项目都完成
ThreadPool.QueueUserWorkItem(state => action());
当操作抛出异常时,我的程序崩溃。处理这种情况的最佳做法是什么
相关:我通常做的是创建一个大的尝试。。。action()方法中的catch块 然后将异常存储为一个私有变量,然后在另一个线程的主线程内处理它(在“排队”的方法中,添加一个try-catch子句……然后在catch中,将捕获的异常放入一个共享异常变量(主线程可见) 然后,在主线程中,当所有排队的项目都完成后(为此使用等待句柄数组),检查某个线程是否使用异常填充了该共享异常…如果是,请重新填充它或根据需要处理它 下面是我最近使用它的一个项目中的一些示例代码…
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;
}
private void complete和queuepayloads(
IEnumerable有效负载,字符串进程ID)
{
List waitHndls=新列表();
int defaultMaxwrkrThreads,defaultmaxIOThreads;
GetMaxThreads(out defaultMaxwrkrThreads,
out defaultmaxIOThreads);
ThreadPool.SetMaxThreads(
MDMImportConfig.maxConcurrentIEUsageRequests,
defaultmaxIOThreads);
int qryNo=0;
foreach(有效载荷中的UsagePayload uPL)
{
ManualResetEvent txEvnt=新的ManualResetEvent(错误);
UsagePayload uPL1=uPL;
int qryNo1=++qryNo;
ThreadPool.QueueUserWorkItem(
代表
{
尝试
{
Thread.CurrentThread.Name=processId+
“+qryNo1;
如果(!HasException&!uPL1.IsComplete)
IEEDAL.GetPayloadReads(上传1,
processId,qryNo1);
如果(!HasException)
UsageCache.PersistPayload(uPL1);
如果(!HasException)
SavePayLoadToProcessQueueFolder(
uPL1,processId,qryNo1);
}
捕获(MeterUsageImportException iX)
{
log.Write(log.Level.Error,
“委托失败”iX.消息,iX);
锁(储物柜)
{
HasException=true;
X=iX;
foreach(手动重置事件
waitHndls中的txEvt)
txEvt.Set();
}
}
最后{lock(locker)txEvnt.Set();}
});
waitHndls.Add(txEvnt);
}
util.WaitAll(waitHndls.ToArray());
线程池.SetMaxThreads(默认MaxWrKrThreads,
defaultmaxIOThreads);
如果(X!=null)抛出X,则锁定(锁定器);
}
如果您有权访问操作的源代码,请在该方法中插入一个try/catch块;否则,创建一个新的tryAction
方法,将对操作的调用封装在try/catch块中。您可以这样添加try/catch:
ThreadPool.QueueUserWorkItem(state =>
{
try
{
action();
}
catch (Exception ex)
{
OnException(ex);
}
});
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);
如果您使用的是.NET4.0,那么可能值得研究该类,因为它可以为您解决这个问题
与原始代码相同但使用任务的代码如下
Task.Factory.StartNew(state => action(), state);
要处理异常,您可以向StartNew返回的任务添加一个延续。它可能如下所示:
ThreadPool.QueueUserWorkItem(state =>
{
try
{
action();
}
catch (Exception ex)
{
OnException(ex);
}
});
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);
简单代码:
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);
}
}
什么是“共享异常变量(对主线程可见)”?如果抛出多个异常怎么办?在上面的示例代码中,X是共享异常变量…它只是在类级别声明的一个变量,所以它在类中的所有方法之间“共享”…一旦抛出第一个异常,您不想停止吗“?+1,非常酷地使用lambda。我发现如果线程池中的工作线程在我的web应用程序中抛出未经处理的异常,它将使整个web服务器崩溃。这救了我!谢谢!有了这个问题,你刚刚救了我的命;)我的IIS正在崩溃,我不知道是什么问题。。。这是一个问题:DU应该考虑使用任务。Task.RunwithErrorHandling的示例请参见