为了账号安全,请及时绑定邮箱和手机立即绑定

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

/ 猿问

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

MM们 2019-08-02 17:26:50

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

简单的搜索DoEvents会带来很多结果,这些结果基本上会导致:

DoEvents是邪恶的。不要使用它。请改用线程。

一般引用的原因是:

  • 重新入侵问题

  • 表现不佳

  • 可用性问题(例如,在禁用的窗口上拖放)

但是一些值得注意的Win32功能,例如TrackPopupMenuDoDragDrop 执行自己的消息处理以保持UI响应,就像这样DoEvents
然而,这些问题似乎都没有遇到过这些问题(表现,再入等等)。

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


查看完整描述

3 回答

?
扬帆大鱼

DoEvents()很危险。但我敢打赌,你每天都会做很多危险的事情。就在昨天,我引爆了一些爆炸装置(未来的读者:请注意相对于某个美国假期的原始发布日期)。小心,我们有时可以解释危险。当然,这意味着了解并了解危险是什么:

  • 重新进入的问题。这里实际上有两个危险:

    1. 这里的部分问题与调用堆栈有关。如果在循环中调用.DoEvents(),该循环本身处理使用DoEvents()的消息,依此类推,则会得到一个非常深的调用堆栈。很容易过度使用DoEvents()并意外填满调用堆栈,从而导致StackOverflow异常。如果你只在一两个地方使用.DoEvents(),你可能还可以。如果它是您长时间运行过程中的第一个工具,那么您可以轻松地在这里遇到麻烦。即使在错误的地方使用一次也可以使用户强制执行stackoverflow异常(有时只需按住enter键),这可能是一个安全问题。

    2. 有时可以在调用堆栈上找到相同的方法两次。如果你没有记住这个方法(提示:你可能没有),那么可能会发生不好的事情。如果传入方法的所有内容都是值类型,并且不依赖于方法之外的东西,那么您可能没问题。但除此之外,您需要仔细考虑如果在调用.DoEvents()之前将控制权返回给您之前再次运行整个方法会发生什么。您的方法之外的哪些参数或资源可能会被修改为您没想到的?您的方法是否更改了任何对象,其中堆栈上的两个实例可能都在同一个对象上?

  • 性能问题。DoEvents()可以给出多线程的错觉,但它不是真正的多线程。这至少有三个真正的危险:

    1. 当您调用DoEvents()时,您将现有线程的控制权交还给消息泵。消息泵可能反过来控制其他东西,而其他东西可能需要一段时间。结果是你的原始操作可能需要更长的时间来完成,而不是它本身的线程永远不会产生控制,绝对比它需要的更长。

    2. 重复工作。因为有可能发现自己运行两次相同的方法,并且我们已经知道这个方法很昂贵/长时间运行(或者你首先不需要DoEvents()),即使你考虑了所提到的所有外部依赖性因此没有不良的副作用,你可能仍然会重复很多工作。

    3. 另一个问题是第一个问题的极端版本:潜在的僵局。如果你的程序中的其他内容取决于你的进程完成,并且会阻塞直到它完成,并且那个东西被来自DoEvents()的消息泵调用,你的应用程序将被卡住并变得无法响应。这可能听起来很牵强,但在实践中,意外地做起来非常容易,并且以后很难找到并调试崩溃。这是您在自己的计算机上可能遇到的一些挂起应用程序情况的根源。

  • 可用性问题。这些副作用是由于不能正确解释其他危险造成的。这里没有什么新东西,只要你适当地看其他地方。

如果你能确定你已经解决了所有这些问题,那就继续吧。但实际上,如果DoEvents()是您首先要解决的UI响应/更新问题,那么您可能无法正确解决所有这些问题。如果它不是你看的第一个地方,还有足够的其他选项,我会质疑你是如何考虑DoEvents()的。

现实情况是,大多数情况下,至少在.Net世界中,BackgroundWorker组件几乎一样容易,至少一次完成一次或两次,它将以安全的方式完成工作。最近,异步/等待模式或使用a Task可以更加有效和安全。


查看完整回答
反对 2019-08-02
?
45度呼吸

我可能错了,但在我看来DoDragDrop并且TrackPopupMenu是相当特殊的情况,因为它们接管了UI,因此没有重入问题(我认为这是人们DoEvents称之为“邪恶”的主要原因)。

就个人而言,我认为将某个功能视为“邪恶”并不合适 - 而是解释陷阱,以便人们可以自行决定。在DoEvents极少数情况下,使用它仍然是合理的,例如在显示模态进度对话框时,用户无法与UI的其余部分进行交互,因此不会出现重入问题。

当然,如果用“邪恶”来表示“你不应该在没有完全理解陷阱的情况下使用的东西”,那么我同意这DoEvents是邪恶的。


查看完整回答
反对 2019-08-02
?
POPMUISE

回到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

对于模态事物(如跟踪弹出窗口),它可能仍然可以。


查看完整回答
反对 2019-08-02
  • 3 回答
  • 0 关注
  • 234 浏览
慕课专栏
更多

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信