如何使用DoEvents()而不是“邪恶”?

一个简单的searchDoEvents带来了很多的结果,基本上,导致:

DoEvents是邪恶的。 不要使用它。 使用线程代替。

一般引用的原因是:

  • 重入问题
  • performance不佳
  • 可用性问题(例如,在禁用的窗口上拖放)

但是一些值得注意的Win32函数(如TrackPopupMenuDoDragDrop 执行自己的消息处理以保持UI的响应,就像DoEvents一样。
但是,这些问题似乎都没有遇到这些问题(performance,重新入侵等)。

他们是如何做到的呢? 他们如何避免DoEvents引用的问题? (或者他们?)

DoEvents()是危险的 。 但是我敢打赌,你每天都会做很多危险的事情。 就在昨天,我掀起了一些爆炸性的装置(未来的读者:注意相对于某个美国节日的原始发布date)。 我们谨慎处理,有时可以解决危险。 当然,这意味着了解和理解危险是什么:

  • 再入境问题。 其实有两个危险:

    1. 这个问题的一部分与调用堆栈有关。 如果你在自己处理使用DoEvents()的消息的循环中调用.DoEvents()等等,你会得到一个相当深的调用栈。 过度使用DoEvents()很容易,并且意外地填满了您的调用堆栈 ,导致StackOverflowexception。 如果你只在一个或两个地方使用.DoEvents(),你可能没问题。 如果这是您长时间运行的第一个工具,那么您很容易在这里遇到麻烦。 即使在错误的地方使用,也可能导致用户强制执行一个栈溢出exception(有时候只是按住回车键),这可能是一个安全问题。
    2. 有时可能会在调用堆栈中find相同的方法两次。 如果你没有build立这个方法(提示:你可能没有),那么可能会发生不好的事情。 如果传递给方法的所有内容都是值types的,并且除了方法之外没有任何依赖关系的东西,那么你可能没有问题。 但是除此之外,您需要仔细考虑如果在调用.DoEvents()的地方将控件返回给您之前,您的整个方法再次运行,会发生什么情况。 您的方法之外的哪些参数或资源可能会被修改,您没有想到? 你的方法是否改变任何对象,堆栈上的两个实例可能作用于同一个对象?
  • 性能问题。 DoEvents()可以给出multithreading的错觉 ,但它不是真正的multithreading。 这至less有三个实际的危险:

    1. 当您调用DoEvents()时,您将现有线程的控制权交还给消息泵。 消息泵可能反过来将控制权交给别的东西,而其他东西可能需要一段时间。 结果是,你原来的操作可能要花费更长的时间才能完成,如果它本身是一个线程本身,从来没有控制,肯定比它需要更长。
    2. 重复的工作。 既然有可能发现自己运行两次相同的方法,并且我们已经知道这个方法是昂贵的/长时间运行的(或者你首先不需要DoEvents()),即使你考虑了所有提到的外部依赖以上所以没有不良的副作用,最终可能会复制大量的工作。
    3. 另一个问题是第一个极端的版本:一个潜在的僵局。 如果程序中的其他内容取决于您的进程完成,并且将阻塞,直到它被DoEvents()中的消息泵调用,那么您的应用程序将会卡住并且无响应。 这可能听起来有些牵强,但实际上意外的事却很容易,事后很难find和debugging崩溃。 这是您在自己的计算机上可能遇到的某些应用程序状况的根源。
  • 可用性问题。 这些是由于没有适当考虑其他危险而产生的副作用。 这里没有什么新东西,只要你在其他地方适当地看。

如果你能确定你占了所有这些东西,那么继续。 但实际上,如果DoEvents()是您想要解决UI响应/更新问题的第一个地方,那么您可能没有正确解决所有这些问题。 如果这不是你看的第一个地方,还有其他的select,我会质疑你是如何考虑DoEvents()的。

实际情况是,大多数情况下,至less在.Net的世界里,一个BackgroundWorker组件几乎一样容易,至less一旦你完成了一次或两次,它将以安全的方式完成这项工作。 最近,asynchronous/等待模式可以更加有效和安全。

回到16位Windows时代,当每个任务共享一个线程时,在一个紧密的循环中保持程序响应的唯一方法是DoEvents 。 正是这种非模态的使用被劝阻于线程。 这是一个典型的例子:

 ' Process image For y = 1 To height For x = 1 to width ProcessPixel x, y End For DoEvents ' <-- DON'T DO THIS -- just put the whole loop in another thread End For 

对于模态的东西(比如跟踪一个popup窗口),它可能还是可以的。

我可能是错的,但在我看来, DoDragDropTrackPopupMenu是比较特殊的情况,因为它们接pipe了UI,因此不存在重入问题(我认为这是人们将DoEvents描述为“Evil”的主要原因) )。

就我个人而言,我认为将这个function解读为“邪恶”是没有帮助的,而是解释一下这个陷阱,让人们可以自己决定。 就DoEvents ,在极less数情况下,使用它仍然是合理的,例如在显示模式进度对话框时,用户不能与其余的UI进行交互,因此不存在重入问题。

当然,如果说“邪恶”是指“如果没有充分理解陷阱,就不应该使用”,那么我认为DoEvents是邪恶的。