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
包含异常详细信息。我相信您可以将调试器配置为不会在
抛出新异常()
行中断。