使用Application.DoEvents()

时间:2012-10-14 11:09:47

标签: c# events asynchronous doevents

我遇到了 Application.DoEvents()可以解决的问题。问题是WebBrowser假设以异步方式导航到一个URL,但它不是,当我使用Application.DoEvents()它解决了这个问题时,我认为这是因为应用程序处理了一些其他事件并且没有提供事件导航正常。

我读了一些关于这个方法的内容,我知道该方法会导致应用程序处理所有的电流事件。现在我有点担心,因为我用大炮杀了一只蚂蚁,有人可以告诉我,我做了什么是值得的吗?

2 个答案:

答案 0 :(得分:4)

是的,Application.DoEvents()解决了这个问题。核心问题是WebBrowser是核心的高度线程化组件。你可以调用它的Navigate()方法,它可以在不阻塞代码的情况下完成它的工作,该方法几乎立即返回。

但问题是,在某些时候它必须运行DocumentCompleted事件。保证在您创建浏览器对象的线程上运行。这很难做到,你的线程可能正忙于做其他事情。就像坐在循环中一样,测试ReadyState属性。没有机制可以中断此循环并运行事件处理程序。

所以你看到的是ReadyState属性永远不会改变,DocumentCompleted事件永远不会触发。这称为死锁,一种非常常见的线程代码诅咒。使用DoEvents是后门,“泵送消息循环”。它允许浏览器进入您的线程并触发事件。这反过来更新ReadyState属性,让你摆脱循环。

然而,DoEvents存在很大问题。它不是选择性的,它不仅限于处理允许事件触发的消息。它还会调度其他通知,这种通知会使程序崩溃。就像您的用户对慢速网站不耐烦并关闭表单一样。这会破坏浏览器对象,但不会停止循环。您现在正在测试已处置浏览器的ReadyState属性。 KABOOM!

你需要以不同的方式做到这一点。在循环中阻止或挂起UI线程永远不合法,它很容易造成死锁。实际上,Microsoft指南针对STA线程禁止。解决方法很简单,将等待循环后现有的代码移动到DocumentCompleted事件处理程序。您可能需要向类中添加一些状态变量,以便您知道事件表示特定网页的完成或用户不再对结果感兴趣。

答案 1 :(得分:1)

Application.Dovents()方法处理所有待处理的邮件。这可能导致:

  1. 在当前的代码块完成之前输入两次代码块。 (假设您通过单击按钮浏览浏览器。用户单击按钮,当您的代码等待浏览器复制用户时再次单击用户。在这种情况下,Application.Doevents()将导致在下一行之前处理该方法。)

  2. 中断关键代码。 (假设您有一个耗时的方法,并且用户点击了关闭按钮。您的表单将消失,但您的代码将继续运行。这是一个真正的问题。

  3. 更多未预料到的结果。

  4. 但是我觉得有时使用这种方法是必要的,并且像webbrowser这样的简单解决方案很难在多线程中使用(特别是当它可见时)。如果你必须使用这种方法,你应该确保用户和其他东西(定时器,按钮,事件vs)不会中断任何事情。 有关详细讨论:Use of Application.DoEvents()