BackgroundWorker在DoWork之前完成

3
我正在使用一个后台工作器来处理文件加载,以防止我的ui被冻结,但似乎RunWorkerCompleted在我的DoWork事件完成之前就已经结束了(导致退出对话框时出现错误)...我有做错什么吗?我最好用任务重新实现这个吗?
public static <T> LoadDesign(string xmlPath)
{
    PleaseWait pw = new PleaseWait(xmlPath);
    pw.ShowDialog();
    return pw.design;
}


private PleaseWait(string xmlFile)
{
   InitializeComponent();
   bw = new BackgroundWorker();
   bw.WorkerSupportsCancellation = true;
   bw.DoWork += (s, e) =>
   {
      design = (Cast)DllCall containing XmlSerializer.Deserialize(...,xmlFile);
   };
   bw.RunWorkerCompleted += (s, e) => {
   //Exit please wait dialog
      this.Close(); 
   };
   if (!bw.IsBusy)
       bw.RunWorkerAsync();
}

我认为问题可能在于我的后台工作程序调用了一个dll但没有等待响应。我已经尝试添加检查,例如while(design == null),但没有成功。 编辑2 错误是NRE,因为设计未加载,我可以轻松解决这个问题,但我宁愿让线程正常工作。

如果您想要得到答案,请提供有关核心代码的完整细节。这个(cast) DllCall太过于伪代码了。 - H H
@HenkHolterman - 我已经添加了这个方法,但我不知道它有什么关联? (同时正在将 Using to dll 加入进程中) - Sayse
我更关心你如何确切地调用它。这是DoWork中唯一相关的事情。 - H H
1
DoWork函数或外部调用中是否有任何异步操作?根据您展示的内容,似乎没有。否则,我无法理解“我的后台工作程序正在调用DLL并且不等待响应”的含义。 - Mike Zboray
然后修复代码并添加任何澄清作为注释。这是关键部分... - H H
显示剩余4条评论
3个回答

7

这里有很多小错误。鉴于我们可能没有看到真正的代码,也没有带有调用堆栈窗口的调试器来查看它在哪里崩溃,其中任何一个可能是一个因素。

  • 测试bw.IsBusy并且在它为true时启动worker是一个严重的错误。在发布的代码中,它永远不可能忙碌,但如果它实际上是true,则你的代码中存在一个严重的错误。由于你确实在繁忙的worker上订阅了事件,现在RunWorkerCompleted事件处理程序将运行两次。

  • 使用Close()方法关闭对话框是不正确的。应该通过分配其DialogResult属性来关闭对话框。这不是最严重的错误,但仍然是错误的。

  • 代码中存在竞争条件,worker可以在对话框显示之前完成。只有当对话框的本机窗口被创建时,才能关闭对话框。换句话说,IsHandleCreated必须为true。您必须进行交错以确保这永远不会发生。订阅对话框的Load事件以启动worker。

  • 您盲目地假设worker将完成工作并产生结果。如果DoWork方法由于异常而死亡,情况将不会如此。这被BackgroundWorker捕获,并作为e.Error属性传递给RunWorkerCompleted事件处理程序。您必须检查此属性,并在其不为null时采取合理的措施。

从评论中可以看出,我猜测最后一条可能是原因。您可以通过使用Debug + Exceptions,选中CLR异常的Thrown复选框来调试它。当抛出异常时,调试器现在会停止,允许您找出问题所在。


谢谢你,汉斯。你说得对,我的假设是错误的。不过,我本来以为会抛出这个错误呢?另外,你有什么参考资料可以告诉我为什么要使用DialogResult吗?我很感兴趣... - Sayse
异常已经被抛出并捕获,因此可以将其传递到RunWorkerCompleted事件处理程序。这是有意设计的,如果BGW经常导致程序崩溃,它就不会那么受欢迎了。我建议阅读任何关于Winforms编程的书籍以了解更多关于对话框的知识。有很多好的书籍可供选择。或者只需单击“提问”按钮即可。 - Hans Passant

2
如果您在后台工作器_DoWork事件中调用另一个异步函数,例如:
    private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        somethingsToDoAsync();
        // somethingsToDoAsync() function is to ASYNC 
    }

_RunWorkerCompleted 在 _Dowork 事件完成之前就会触发。

将其他 somethingsToDoAsync() 函数改为非异步。


1
也许你的后台工作实际上并不需要太多时间,在对话框显示之前就已经完成了。我建议将后台工作的初始化和启动代码转移到PleaseWait的Form_Load或Form_Shown中。

1
我甚至会将其转移到 Shown,因为这是在表单显示时触发的事件。Load 可能会在 Shown 之前被调用。 - Thorsten Dittmar
我向YK1道歉,错误与form.Close无关,但我可以理解为什么会这样看,并感谢您的回答。我会更新问题。 - Sayse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接