¿Cómo usar DoEvents () sin ser “malvado”?

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?)

    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 stack de llamadas. Si llama a .DoEvents () en un bucle que maneja mensajes que usan DoEvents (), y así sucesivamente, obtiene una stack de llamadas bastante profunda. Es fácil usar en exceso DoEvents () y accidentalmente llenar su stack de llamadas , lo que resulta en una excepción de StackOverflow. 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 stackoverflow (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 stack 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 su método completo 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 stack 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 (), le 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 hay algo más en su progtwig que 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 lockings 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 en realidad, si DoEvents () es el primer lugar que busca para resolver problemas de respuesta / actualización de la UI, probablemente no esté teniendo 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 o el uso de una Task puede ser mucho más efectivo y seguro.

    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 progtwig 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 IU, por lo que no tienen el problema de reentrada (que creo que es la razón principal por la que las personas describen a DoEvents como “Malvado”). )

    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 rest 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.