BackgroundWorker中的异步错误处理

时间:2013-02-09 21:06:47

标签: multithreading winforms exception-handling event-handling backgroundworker

BackgroundWorker.Error必须调用可能会抛出异常的委托时,如何获得有意义的DoWork

我正在实现一个静态MsgBox类,它公开了向用户传达自定义消息的各种方式(使用自定义消息表单)。

其中一个公开的方法ShowProgress实例化ProgressMessageBoxForm(派生自自定义MessageBoxForm),在发生某些后台操作时显示进度条(让用户取消操作)与此同时)。如果在模态窗体上运行后台任务听起来很尴尬,请考虑这样的签名:

public static DialogResult ShowProgress(string title, string message, _
    Action<AsyncProgressArgs> progressAction)

这个想法是封装一个后台工作程序,它将其“工作”委托给任何提供的方法(/ handler),同时允许该方法“对话”并报告进度和取消...以及理想的错误状态。在此上下文中,表单是否与模态无关,能够在可取消模式表单上的进度栏中显示正在运行的任务的进度,该表单也会自动关闭,除非取消选中复选框以保持可见并显示完成后的成功状态是必需的功能。

以下是有问题的AsyncProgressArgs课程:

public class AsyncProgressArgs
{
    private readonly Action<int, string> _update;
    private readonly BackgroundWorker _worker;
    private readonly DoWorkEventArgs _workEventArgs;

    public AsyncProgressArgs(Action<int, string> updateProgress, BackgroundWorker worker, DoWorkEventArgs e)
    {
        _update = updateProgress;
        _worker = worker;
        _workEventArgs = e;
    }

    /// <summary>
    /// Reports progress to underlying <see cref="BackgroundWorker"/>.
    /// Increments <see cref="ProgressBar"/> value by specified <see cref="int"/> amount and
    /// updates the progress <see cref="Label"/> with specified <see cref="string"/> caption.
    /// </summary>
    public Action<int, string> UpdateProgress { get { return _update; } }

    /// <summary>
    /// Verifies whether asynchronous action is pending cancellation,
    /// in which case asynchronous operation gets cancelled.
    /// </summary>
    public void CheckCancelled()
    {
        _workEventArgs.Cancel = _worker.CancellationPending;
    }
}

BackgroundWorker的{​​{1}}事件处理程序然后调用传递给自定义消息框的方法

DoWork

给出如下方法:

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

一切都按预期工作(进度条移动,进程可以取消),直到在action方法中抛出异常。通常MsgBox.ShowProgress("Async Test", "Please wait while operation completes.", _ AsyncProgressAction); 会捕获它并将其存储在BackgroundWorker属性中,但这不会发生。

因此,传递的action方法中的代码需要处理它自己的异常,如果没有,它仍然没有处理,程序死了可怕的死亡。

问题是,是否有可能拥有这样的结构,并且在进程完成时仍能以某种方式获得有意义的Error属性?我希望能够在action方法中的任何位置Error并在封装的worker中处理它。

旁注,我使用了Throw new Exception(),因为BackgroundWorker我无法获得进度条,直到完成所有工作,我想避免处理{{1对象实例直接。

修改 这是一个非问题,编译的应用程序不会爆炸。实际上,我对调试器打破了委托方法中抛出的异常感到困惑。正如下面的评论中所指出的,执行/调试可以之后继续,并且无论运行什么错误处理逻辑,都将运行。我期待Task以某种方式Thread异常和调试器继续前进,但事实证明异常被捕获并且调试器仍在中断。

我之前应该读过这个:Unhandled exceptions in BackgroundWorker

1 个答案:

答案 0 :(得分:1)

我之前应该读过这个:Unhandled exceptions in BackgroundWorker

这是一个非常非常长的问题,有很多上下文可以解决一个简单的非问题:VS调试器停止并误导性地说“Exception未被用户代码处理”,因为它会处理未处理的异常......当它实际上意味着“BackgroundWorker任务抛出异常,调试器让你知道它,否则你会认为BackgroundWorker正在吞下它。”

可以F5 /忽略异常以恢复执行,异常 按预期在RunWorkerCompletedEventArgs.Error结束,并且部署的应用在用户的脸上爆炸。

发布此答案可以从(溢出?)未回答的问题堆栈中删除此问题......