gpt4 book ai didi

c# - 为什么BackgroundWorker RunWorkerCompleted事件不能重新抛出异常

转载 作者:行者123 更新时间:2023-11-30 14:52:56 25 4
gpt4 key购买 nike

我查看了一些关于这个主题的帖子,其中包括 如何解决这个问题,但我不太明白 为什么这种奇怪的行为?

短版 :

为什么是 Exception s 扔进了RunWorkerCompleted事件未被调用代码捕获?

详细版 :

  • 我有一个 BackgroundWorker (从现在开始的BGW)。
  • BGW的DoWork事件引发 Exception
  • BGW的RunWorkerCompleted事件捕获 Exception ,记录并做一些很酷的清理工作。
  • 清理后,RunWorkerCompleted事件重新抛出 Exception .

  • 如果 RunWorkerCompleted事件在主线程上运行,这是否意味着调用代码(也在主线程上)应该能够捕获该异常?

    一些代码来加强这个概念......
    private void SomeMethod()
    {
    BackgroundWorker bgw = new BackgroundWorker();
    bgw.DoWork += bgw_DoWork;
    bgw.RunWorkerCompleted += bgw_RunWorkerCompleted;
    bgw.RunWorkerAsync();
    }

    private void bgw_DoWork(object sender, DoWorkEventArgs e)
    {
    throw new Exception("Oops");
    }

    private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
    if(e.Error != null)
    {
    // Log exception & cleanup code here...
    throw e.Error; // Always unhandled :(
    }
    }

    我假设调用 SomeMethod像这样会捕获 Exception并显示消息框,但 BGW 的行为不像我预期的那样, Exception总是得不到处理……
    try
    {
    SomeMethod();
    }
    catch (Exception)
    {
    MessageBox.Show("Handled exception");
    }

    最佳答案

    ... not caught by the calling code?



    “调用代码”是您问题中的重要细节,究竟是谁调用了您的 RunWorkerCompleted 事件处理程序?您知道它不可能是您的代码,您从未编写过任何显式调用事件处理程序的代码。您所做的就是订阅该事件。所以你知道那里有麻烦,因为它不是你的代码,你不可能编写一个 try/catch 来捕获那个异常并处理它。

    我强烈建议你看看,在你的事件处理程序上设置一个断点,当它中断时,查看调试器的调用堆栈窗口,看看它是如何到达那里的。请记住,这将是 .NET Framework 代码,您需要关闭 Just My Code 调试器选项,以便查看所有内容。

    首先是不常见的情况,在控制台应用程序或服务中,调用事件处理程序的是 SychronizationContext.Post()。代码在任意线程池线程上运行,并且没有任何 catch 语句可以捕获此异常。您的应用程序将始终终止。

    一个常见的情况是 Winforms,您在调用堆栈窗口中看到的与您的代码有任何关系的最后一条语句是在 Main() 方法中调用 Application.Run()。您的事件处理程序是由对 Control.BeginInvoke() 的调用触发的,您看不到它,但这就是您的事件处理程序代码最终在 UI 线程上运行的方式。 Winforms 在其 Run() 方法中有一个 backstop catch 语句,它会捕获并引发 Application.ThreadException 事件。它仅在您不使用调试器时才处于事件状态。如果您没有以其他方式订阅您自己的事件处理程序,则默认处理程序会显示 ThreadExceptionDialog 对话框,该对话框使用户可以选择单击 Continue 或 Quit。让它走得那么远并不是一个好主意,你的用户没有像样的方法来选择正确的选择,除了反复试验。请注意调试器所扮演的角色,如果没有调试器,它将以不同的方式工作,并且会直接引发 ThreadException 事件。

    下一个常见情况是 WPF,它的工作方式与 Winforms 非常相似,并且您的事件处理程序由对 Dispatcher.BeginInvoke() 的调用触发。它的调度程序循环中也有一个 catch 语句。同样,它引发 DispatcherUnhandledException 事件。它没有像 Winforms 那样的该事件的默认处理程序,如果您不订阅,则应用程序将终止。

    因此,一般来说,不可避免的结果是,当重新引发异常时,您的应用程序将终止。周围没有任何仙女教母愿意处理你不想处理的问题。处理这样的异常通常是有问题的,您几乎不知道 DoWork 事件处理程序中实际出了什么问题。给定它无法处理异常,否则它会捕获它。你可以稍后再做并且正确做的几率会随着从抛出的语句中删除的越远而迅速减少。

    您在 RunWorkerCompleted 事件处理程序中真正知道的是“它不起作用”。处理这样的异常是有风险的,你不知道你的程序状态有多少被失败的代码改变了。不过有一个很大的优势,您在 DoWork 中放入的代码本质上是非常松散耦合的。非常重要的,在工作线程上运行的紧密耦合的代码几乎是不可能编写的,您很少能正确获得所需的锁定。

    在实践中,您执行一个根本不会改变任何状态的操作。就像使用查询结果填充列表的 dbase 查询一样。您在事件处理程序中使用 e.Result 属性来应用后台操作的结果。因此,当它不起作用时并没有造成真正的伤害,你只是没有得到结果。你必须让用户知道这件事,毕竟他没有得到他期望的结果。并且用户经常不得不调用某人来纠正问题,希望不是你,而是解决根本问题的 IT 人员。 MessageBox.Show() 完成了这项工作。

    关于c# - 为什么BackgroundWorker RunWorkerCompleted事件不能重新抛出异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30728702/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com