windows winforms winapi visual-c++ doevents

windows - ¿Cómo usar DoEvents() sin ser "malvado"?



winforms winapi (3)

Una simple búsqueda de DoEvents trae muchos resultados que conducen, básicamente, a:

DoEvents es malo. No lo uses Use enhebrar en su lugar.

Las razones generalmente citadas son:

  • Problemas de reentrada
  • Bajo rendimiento
  • Problemas de usabilidad (por ejemplo, arrastrar / soltar sobre una ventana deshabilitada)

Pero algunas funciones notables de Win32, como TrackPopupMenu y DoDragDrop realizan su propio procesamiento de mensajes para mantener la UI receptiva, al igual que DoEvents .
Y, sin embargo, ninguno de estos parece encontrar estos problemas (rendimiento, reentrada, etc.).

¿Cómo lo hicieron? ¿Cómo evitan los problemas citados con DoEvents ? (¿O ellos?)


De vuelta en los días de Windows de 16 bits, cuando cada tarea compartía un único hilo, la única forma de mantener un programa receptivo dentro de un circuito cerrado era DoEvents . Es este uso no modal el que se desalienta a favor de los hilos. Aquí hay un ejemplo típico:

'' 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

Para las cosas modales (como el seguimiento de una ventana emergente), es probable que todavía esté bien.


Puedo estar equivocado, pero me parece que DoDragDrop y TrackPopupMenu son casos bastante especiales, ya que se apoderan de la interfaz de usuario, por lo que no tienen el problema de reentrada (que creo que es la razón principal por la que las personas describen DoEvents como "Evil"). )

Personalmente, no creo que sea útil desestimar una característica como "Evil", sino más bien explicar las trampas para que las personas puedan decidir por sí mismas. En el caso de DoEvents existen raros casos en los que todavía es razonable usarlo, por ejemplo, mientras se muestra un cuadro de diálogo de progreso modal, donde el usuario no puede interactuar con el resto de la IU, por lo que no hay problemas de reentrada.

Por supuesto, si con "Evil" te refieres a "algo que no deberías usar sin entender completamente las trampas", entonces estoy de acuerdo con que DoEvents es malo.


DoEvents () es peligroso . Pero apuesto a que haces muchas cosas peligrosas todos los días. Ayer mismo prendí algunos artefactos explosivos (futuros lectores: fíjese en la fecha de publicación original relativa a ciertas festividades estadounidenses). Con cuidado, a veces podemos dar cuenta de los peligros. Por supuesto, eso significa conocer y comprender cuáles son los peligros:

  • Problemas de reingreso En realidad, hay dos peligros aquí:

    1. Parte del problema aquí tiene que ver con la pila de llamadas. Si llama a .DoEvents () en un bucle que maneja mensajes que usan DoEvents (), y así sucesivamente, obtiene una pila de llamadas bastante profunda. Es fácil usar en exceso DoEvents () y accidentalmente llenar su pila de llamadas , lo que resulta en una excepción de . Si solo está utilizando .DoEvents () en uno o dos lugares, probablemente esté bien. Si es la primera herramienta a la que recurre cada vez que tiene un proceso de larga duración, puede encontrarse fácilmente en problemas aquí. Incluso un solo uso en el lugar equivocado puede hacer posible que un usuario fuerce una excepción de (a veces solo manteniendo presionada la tecla enter), y eso puede ser un problema de seguridad.
    2. A veces es posible encontrar el mismo método en la pila de llamadas dos veces. Si no construiste el método con esto en mente (pista: probablemente no lo hiciste), entonces pueden suceder cosas malas. Si todo lo que se transfirió al método es un tipo de valor y no hay dependencia de las cosas fuera del método, puede estar bien. Pero, de lo contrario, debe pensar cuidadosamente qué sucede si todo su método se ejecutara nuevamente antes de que se le devuelva el control en el punto donde se llama a .DoEvents (). ¿Qué parámetros o recursos fuera de su método podrían modificarse que no esperaba? ¿Su método cambia cualquier objeto, donde ambas instancias en la pila podrían estar actuando en el mismo objeto?
  • Problemas de desempeño. DoEvents () puede dar la ilusión de multi-threading, pero no es verdadero mutlithreading. Esto tiene al menos tres peligros reales:

    1. Cuando llama a DoEvents (), está devolviendo el control de su hilo existente a la bomba de mensajes. La bomba de mensajes podría a su vez dar el control a otra cosa, y esa otra cosa podría tomar un tiempo. El resultado es que su operación original podría tomar más tiempo para terminar que si estuviera en un hilo en sí mismo que nunca ceda el control, definitivamente más tiempo de lo que necesita.
    2. Duplicación del trabajo. Dado que es posible que se encuentre ejecutando el mismo método dos veces, y ya sabemos que este método es costoso / de larga ejecución (o no necesitaría DoEvents () en primer lugar), incluso si contabilizara todas las dependencias externas mencionadas arriba para que no haya efectos secundarios adversos, puede terminar duplicando mucho trabajo.
    3. El otro problema es la versión extrema de la primera: un potencial de estancamiento. Si algo más en su programa depende de su proceso de finalización, y se bloqueará hasta que lo haga, y la bomba de mensajes de DoEvents () llamará a esa cosa, su aplicación se quedará atascada y no responderá. Esto puede sonar exagerado, pero en la práctica es sorprendentemente fácil de hacer accidentalmente, y los bloqueos son muy difíciles de encontrar y depurar después. Esta es la raíz de algunas de las situaciones de aplicaciones colgadas que puede haber experimentado en su propia computadora.
  • Problemas de usabilidad. Estos son efectos secundarios que resultan de no dar cuenta de los otros peligros. No hay nada nuevo aquí, siempre y cuando haya buscado en otros lugares de manera adecuada.

Si puede estar seguro de que explica todas estas cosas, continúe. Pero realmente, si DoEvents () es el primer lugar que busca para resolver los problemas de respuesta / actualización de la UI, probablemente no tenga en cuenta todos estos problemas correctamente. Si no es el primer lugar donde miras, hay suficientes otras opciones que cuestionaría cómo llegaste a considerar DoEvents () en absoluto.

La realidad es que la mayoría de las veces, al menos en el mundo .Net, un componente de BackgroundWorker es casi tan fácil, al menos una vez que lo ha hecho una o dos veces, y hará el trabajo de una manera segura. Más recientemente, el patrón async / await puede ser mucho más efectivo y seguro.