Multithreading BackgroundWorker中的异步错误处理
当Multithreading BackgroundWorker中的异步错误处理,multithreading,winforms,exception-handling,event-handling,backgroundworker,Multithreading,Winforms,Exception Handling,Event Handling,Backgroundworker,当DoWork必须调用可能抛出exeption的委托时,如何获得有意义的BackgroundWorker.Error 我正在实现一个静态的MsgBox类,它公开了向用户传递自定义消息的各种方式(使用自定义消息表单) 其中一个公开的方法,ShowProgress,实例化了一个ProgressMessageBoxForm(源自自定义的MessageBoxForm),它在执行某些后台操作时显示一个进度条(允许用户随时取消操作)。如果在一个模态表单上运行后台任务听起来很尴尬,请考虑这样的签名: publ
DoWork
必须调用可能抛出exeption的委托时,如何获得有意义的BackgroundWorker.Error
我正在实现一个静态的MsgBox
类,它公开了向用户传递自定义消息的各种方式(使用自定义消息表单)
其中一个公开的方法,ShowProgress
,实例化了一个ProgressMessageBoxForm
(源自自定义的MessageBoxForm
),它在执行某些后台操作时显示一个进度条(允许用户随时取消操作)。如果在一个模态表单上运行后台任务听起来很尴尬,请考虑这样的签名:
public static DialogResult ShowProgress(string title, string message, _
Action<AsyncProgressArgs> progressAction)
MsgBox.ShowProgress("Async Test", "Please wait while operation completes.", _
AsyncProgressAction);
然后,BackgroundWorker
的DoWork
事件处理程序调用传递给自定义messagebox的方法
protected virtual void worker_DoWork(object sender, DoWorkEventArgs e)
{
// *** If RunWorkerCompletedEventArgs.Error caught exceptions,
// then this try/catch block wouldn't be needed:
// try
// {
_startHandler(this, new AsyncProgressArgs(UpdateProgressAsync, _worker, e));
// }
// catch(Exception exception)
// {
// if (MsgBox.Show(exception) == DialogResult.Retry)
// {
// BeginWork(_startHandler);
// }
// else
// {
// Hide();
// }
// }
}
给出这样一种方法:
private void AsyncProgressAction(AsyncProgressArgs e)
{
// do some work:
Thread.Sleep(200);
// increment progress bar value and change status message:
e.UpdateProgress(10, "Operation #1 completed.");
// see if cancellation was requested:
e.CheckCancelled();
// do some work:
Thread.Sleep(500);
// increment progress bar value and change status message:
e.UpdateProgress(30, "Operation #2 completed.");
// see if cancellation was requested:
e.CheckCancelled();
// ...
// throw new Exception("This should be caught by the BackgroundWorker");
}
调用代码可以如下所示:
public static DialogResult ShowProgress(string title, string message, _
Action<AsyncProgressArgs> progressAction)
MsgBox.ShowProgress("Async Test", "Please wait while operation completes.", _
AsyncProgressAction);
在action方法中抛出异常之前,一切都按预期进行(进度条移动,进程可以取消)。通常,BackgroundWorker
会捕获它并将其存储在其Error
属性中,但在这里不会发生这种情况
因此,传递的action方法中的代码需要处理自己的异常,如果不处理,它将保持未处理状态,程序将可怕地死亡
问题是,是否有可能拥有这样一个构造,并且在流程完成时仍然能够以某种方式拥有一个有意义的Error
属性?我希望能够在action方法中的任何位置抛出newexception()
,并在封装的worker中处理它
旁注,我求助于BackgroundWorker
,因为使用Task
在所有工作完成之前,我无法移动进度条,我希望避免直接处理线程
对象实例
编辑
这不是问题,编译后的应用程序不会爆炸。实际上,我被调试程序在委托方法中抛出的异常上的中断弄糊涂了。正如下面的注释所指出的,执行/调试可以在之后继续,并且无论打算运行什么错误处理逻辑,都将运行。我原以为BackgroundWorker会以某种方式捕获异常,调试器会继续运行,但结果发现异常被捕获,调试器仍然会中断
我以前应该读过这篇文章:我以前应该读过这篇文章: 对于一个简单的非问题,这是一个非常、非常长的问题,有很多上下文:VS调试器停止并误导性地说“异常未由用户代码处理”,就像对未处理的异常一样。。。当它实际上意味着“BackgroundWorker任务抛出了一个异常,调试器会让您知道,否则您会认为BackgroundWorker正在吞噬它。” 异常可以被F5'd/忽略以恢复执行,异常最终会出现在
RunWorkerCompletedEventArgs.Error中,正如预期的那样,并且部署的应用不会在用户面前爆炸
发布此答案以从(溢出?)未回答问题堆中删除此问题…我不确定您为什么要定义自己的EventHandler
和EventArgs
以支持进度更新和取消。BackgroundWorker
类提供了执行此操作所需的所有事件和方法。@Ginosaji谢谢,你说得对,我已将EventHandler委托切换为Action委托,将EventArgs派生类型切换为独立的args类。这样更干净,但问题仍然是我不能信任BackgroundWorker
拾取委托方法体中发生的异常,这给我留下了一个毫无价值的RunWorkerCompletedEventArgs.Error
和用try/catch包装调用的义务。在DoWork
处理程序中重新调用异常也没有帮助。我刚刚编写了一个快速进度对话框,似乎无法重现此问题。即使当DoWork
处理程序调用引发异常的委托时,它也存储在RunWorkerCompletedEventArgs.Error
中。。。我的调试器在throw new Exception()
行中断。我的调试器也是如此,但我只需按continue,它就会继续运行到RunWorkerCompleted
中的断点,其中e.Error
包含异常详细信息。我相信您可以将调试器配置为不会在抛出新异常()
行中断。