3 回答
TA贡献1799条经验 获得超9个赞
DoEvents()很危险。但我敢打赌,你每天都会做很多危险的事情。就在昨天,我引爆了一些爆炸装置(未来的读者:请注意相对于某个美国假期的原始发布日期)。小心,我们有时可以解释危险。当然,这意味着了解并了解危险是什么:
重新进入的问题。这里实际上有两个危险:
这里的部分问题与调用堆栈有关。如果在循环中调用.DoEvents(),该循环本身处理使用DoEvents()的消息,依此类推,则会得到一个非常深的调用堆栈。很容易过度使用DoEvents()并意外填满调用堆栈,从而导致StackOverflow异常。如果你只在一两个地方使用.DoEvents(),你可能还可以。如果它是您长时间运行过程中的第一个工具,那么您可以轻松地在这里遇到麻烦。即使在错误的地方使用一次也可以使用户强制执行stackoverflow异常(有时只需按住enter键),这可能是一个安全问题。
有时可以在调用堆栈上找到相同的方法两次。如果你没有记住这个方法(提示:你可能没有),那么可能会发生不好的事情。如果传入方法的所有内容都是值类型,并且不依赖于方法之外的东西,那么您可能没问题。但除此之外,您需要仔细考虑如果在调用.DoEvents()之前将控制权返回给您之前再次运行整个方法会发生什么。您的方法之外的哪些参数或资源可能会被修改为您没想到的?您的方法是否更改了任何对象,其中堆栈上的两个实例可能都在同一个对象上?
性能问题。DoEvents()可以给出多线程的错觉,但它不是真正的多线程。这至少有三个真正的危险:
当您调用DoEvents()时,您将现有线程的控制权交还给消息泵。消息泵可能反过来控制其他东西,而其他东西可能需要一段时间。结果是你的原始操作可能需要更长的时间来完成,而不是它本身的线程永远不会产生控制,绝对比它需要的更长。
重复工作。因为有可能发现自己运行两次相同的方法,并且我们已经知道这个方法很昂贵/长时间运行(或者你首先不需要DoEvents()),即使你考虑了所提到的所有外部依赖性因此没有不良的副作用,你可能仍然会重复很多工作。
另一个问题是第一个问题的极端版本:潜在的僵局。如果你的程序中的其他内容取决于你的进程完成,并且会阻塞直到它完成,并且那个东西被来自DoEvents()的消息泵调用,你的应用程序将被卡住并变得无法响应。这可能听起来很牵强,但在实践中,意外地做起来非常容易,并且以后很难找到并调试崩溃。这是您在自己的计算机上可能遇到的一些挂起应用程序情况的根源。
可用性问题。这些副作用是由于不能正确解释其他危险造成的。这里没有什么新东西,只要你适当地看其他地方。
如果你能确定你已经解决了所有这些问题,那就继续吧。但实际上,如果DoEvents()是您首先要解决的UI响应/更新问题,那么您可能无法正确解决所有这些问题。如果它不是你看的第一个地方,还有足够的其他选项,我会质疑你是如何考虑DoEvents()的。
现实情况是,大多数情况下,至少在.Net世界中,BackgroundWorker组件几乎一样容易,至少一次完成一次或两次,它将以安全的方式完成工作。最近,异步/等待模式或使用a Task
可以更加有效和安全。
TA贡献1836条经验 获得超13个赞
我可能错了,但在我看来DoDragDrop
并且TrackPopupMenu
是相当特殊的情况,因为它们接管了UI,因此没有重入问题(我认为这是人们DoEvents
称之为“邪恶”的主要原因)。
就个人而言,我认为将某个功能视为“邪恶”并不合适 - 而是解释陷阱,以便人们可以自行决定。在DoEvents
极少数情况下,使用它仍然是合理的,例如在显示模态进度对话框时,用户无法与UI的其余部分进行交互,因此不会出现重入问题。
当然,如果用“邪恶”来表示“你不应该在没有完全理解陷阱的情况下使用的东西”,那么我同意这DoEvents
是邪恶的。
TA贡献1765条经验 获得超5个赞
回到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
对于模态事物(如跟踪弹出窗口),它可能仍然可以。
- 3 回答
- 0 关注
- 724 浏览
添加回答
举报