如何在不“邪恶"的情况下使用 DoEvents()? [英] How to use DoEvents() without being "evil"?

查看:42
本文介绍了如何在不“邪恶"的情况下使用 DoEvents()?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

一个简单的搜索DoEvents会带来很多结果,基本上,到:

A simple search for DoEvents brings up lots of results that lead, basically, to:

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

DoEvents is evil. Don't use it. Use threading instead.

通常引用的原因是:

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

但是一些值得注意的 Win32 函数,例如 TrackPopupMenuDoDragDrop 执行自己的消息处理以保持 UI 响应,就像 DoEvents 可以.
然而,这些似乎都没有遇到这些问题(性能、重入等).

But some notable Win32 functions such as TrackPopupMenu and DoDragDrop perform their own message processing to keep the UI responsive, just like DoEvents does.
And yet, none of these seem to come across these issues (performance, re-entrancy, etc.).

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

How do they do it? How do they avoid the problems cited with DoEvents? (Or do they?)

推荐答案

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

DoEvents() is dangerous. But I bet you do lots of dangerous things every day. Just yesterday I set off a few explosive devices (future readers: note the original post date relative to a certain American holiday). With care, we can sometimes account for the dangers. Of course, that means knowing and understanding what the dangers are:

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

  • Re-entry issues. There are actually two dangers here:

  1. 这里的部分问题与调用堆栈有关.如果您在循环中调用 .DoEvents(),该循环本身处理使用 DoEvents() 的消息,依此类推,您将获得一个非常深的调用堆栈.很容易过度使用 DoEvents() 并意外填满您的调用堆栈,从而导致 StackOverflow 异常.如果你只在一两个地方使用 .DoEvents() ,你可能没问题.如果它是您在长时间运行的过程中使用的第一个工具,那么您很容易在这里遇到麻烦.即使在错误的地方使用一次,也可能使用户强制一个 stackoverflow 异常(有时只需按住 Enter 键),这可能是一个安全问题.
  2. 有时可能会在调用堆栈中两次找到相同的方法.如果您没有考虑到这一点来构建方法(提示:您可能没有),那么可能会发生不好的事情.如果传递给该方法的所有内容都是值类型,并且不依赖于该方法之外的内容,则可能没问题.但除此之外,您需要仔细考虑如果在调用 .DoEvents() 的地方将控制权返回给您之前再次运行整个方法会发生什么情况.方法之外的哪些参数或资源可能会被修改,这是您没有预料到的?您的方法是否更改了任何对象,其中堆栈上的两个实例可能作用于同一个对象?
  1. Part of the problem here has to do with the call stack. If you call .DoEvents() in a loop that itself handles messages that use DoEvents(), and so on, you're getting a pretty deep call stack. It's easy to over-use DoEvents() and accidentally fill up your call stack, resulting in a StackOverflow exception. If you're only using .DoEvents() in one or two places, you're probably okay. If it's the first tool you reach for whenever you have a long-running process, you can easily find yourself in trouble here. Even one use in the wrong place can make it possible for a user to force a stackoverflow exception (sometimes just by holding down the enter key), and that can be a security issue.
  2. It is sometimes possible to find your same method on the call stack twice. If you didn't build the method with this in mind (hint: you probably didn't) then bad things can happen. If everything passed in to the method is a value type, and there is no dependance on things outside of the method, you might be fine. But otherwise, you need to think carefully about what happens if your entire method were to run again before control is returned to you at the point where .DoEvents() is called. What parameters or resources outside of your method might be modified that you did not expect? Does your method change any objects, where both instances on the stack might be acting on the same object?

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

  • Performance Issues. DoEvents() can give the illusion of multi-threading, but it's not real mutlithreading. This has at least three real dangers:

    1. 当您调用 DoEvents() 时,您将现有线程的控制权交还给消息泵.消息泵可能反过来将控制权交给其他东西,而其他东西可能需要一段时间.结果是您的原始操作可能需要更长的时间才能完成,如果它本身在一个永远不会产生控制的线程中,肯定比它需要的时间更长.
    2. 重复工作.由于可能会发现自己两次运行相同的方法,而且我们已经知道这种方法很昂贵/需要长时间运行(或者您一开始就不需要 DoEvents()),即使您考虑了提到的所有外部依赖项以上,所以没有不利的副作用,您最终可能仍会重复大量工作.
    3. 另一个问题是第一个问题的极端版本​​:可能会陷入僵局.如果您的程序中的其他内容取决于您的进程完成,并且会阻塞直到它完成,并且该内容被来自 DoEvents() 的消息泵调用,您的应用程序将卡住并变得无响应.这听起来可能有些牵强,但在实践中意外地很容易做到,而且以后很难找到和调试崩溃.这是您在自己的计算机上可能遇到的一些应用挂起情况的根源.

  • 可用性问题.这些是由于没有正确考虑其他危险而导致的副作用.只要您适当地查看其他地方,这里就没有什么新东西了.

  • Usability Issues. These are side-effects that result from not properly accounting for the other dangers. There's nothing new here, as long as you looked in other places appropriately.

    如果你可以确定你已经考虑了所有这些事情,那么继续.但实际上,如果 DoEvents() 是您寻求解决 UI 响应性/更新问题的第一个地方,那么您可能没有正确考虑所有这些问题.如果这不是您首先看到的地方,那么还有足够多的其他选项,我会质疑您是如何考虑 DoEvents() 的.今天,DoEvents() 的存在主要是为了与在其他可用选项之前出现的旧代码兼容,并作为新程序员的拐杖,他们还没有获得足够的经验来接触其他选项.

    If you can be sure you accounted for all these things, then go ahead. But really, if DoEvents() is the first place you look to solve UI responsiveness/updating issues, you're probably not accounting for all of those issues correctly. If it's not the first place you look, there are enough other options that I would question how you made it to considering DoEvents() at all. Today, DoEvents() exists mainly for compatibility with older code that came into being before other credible options where available, and as a crutch for newer programmers who haven't yet gained enough experience for exposure to the other options.

    现实情况是,在大多数情况下,至少在 .Net 世界中,BackgroundWorker 组件几乎同样简单,至少在您完成一两次之后,它将以安全的方式完成工作.最近,async/await 模式 或使用 Task 可以更加有效和安全,而无需深入研究自行编写多线程代码.

    The reality is that most of the time, at least in the .Net world, a BackgroundWorker component is nearly as easy, at least once you've done it once or twice, and it will do the job in a safe way. More recently, the async/await pattern or the use of a Task can be much more effective and safe, without needing to delve into full-blown multi-threaded code on your own.

    这篇关于如何在不“邪恶"的情况下使用 DoEvents()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆