Come usare DoEvents () senza essere “malvagio”?

Una semplice ricerca di DoEvents porta a molti risultati che portano, in sostanza, a:

DoEvents è malvagio. Non usarlo Utilizzare invece la filettatura.

I motivi generalmente citati sono:

  • Problemi di re-entrancy
  • Scarse prestazioni
  • Problemi di usabilità (ad esempio trascinamento / rilascio su una finestra disabilitata)

Ma alcune funzioni di Win32 degne di nota come TrackPopupMenu e DoDragDrop eseguono la loro elaborazione dei messaggi per mantenere l’interfaccia utente retriggers, proprio come fa DoEvents .
Eppure, nessuno di questi sembra imbattersi in questi problemi (performance, re-entrancy, ecc.).

Come lo fanno? Come evitano i problemi citati con DoEvents ? (O lo fanno?)

    DoEvents () è pericoloso . Ma scommetto che fai molte cose pericolose ogni giorno. Proprio ieri ho messo in moto alcuni ordigni esplosivi (futuri lettori: annota la data del post originale relativa ad una certa festa americana). Con cura, possiamo a volte spiegare i pericoli. Certo, questo significa conoscere e capire quali sono i pericoli:

    • Problemi di rientro. Ci sono in realtà due pericoli qui:

      1. Parte del problema qui ha a che fare con lo stack delle chiamate. Se si chiama .DoEvents () in un ciclo che gestisce i messaggi che utilizzano DoEvents () e così via, si ottiene uno stack di chiamate piuttosto intenso. È facile sovradimensionare DoEvents () e riempire accidentalmente lo stack delle chiamate , risultando in un’eccezione StackOverflow. Se usi solo .DoEvents () in uno o due posti, probabilmente stai bene. Se è il primo strumento a cui puoi accedere ogni volta che hai un processo di lunga durata, puoi facilmente trovarti nei guai qui. Anche un solo utilizzo nel posto sbagliato può consentire a un utente di forzare un’eccezione di stackoverflow (a volte semplicemente tenendo premuto il tasto Invio), e questo può essere un problema di sicurezza.
      2. Talvolta è ansible trovare lo stesso metodo nello stack di chiamate due volte. Se non hai costruito il metodo con questo in mente (suggerimento: probabilmente non l’hai fatto) allora possono accadere cose brutte. Se tutto è passato al metodo è un tipo di valore, e non c’è dipendenza da cose al di fuori del metodo, potrebbe andare bene. In caso contrario, è necessario riflettere attentamente su ciò che accade se l’intero metodo dovesse essere eseguito nuovamente prima che il controllo venga restituito al punto in cui viene chiamato. DoEvents (). Quali parametri o risorse al di fuori del tuo metodo potrebbero essere modificati che non ti aspettavi? Il tuo metodo cambia oggetti, dove entrambe le istanze nello stack potrebbero agire sullo stesso object?
    • Problemi di prestazione. DoEvents () può dare l’ illusione di multi-threading, ma non è un reale mutlithreading. Questo ha almeno tre pericoli reali:

      1. Quando si chiama DoEvents (), si restituisce il controllo sul thread esistente al pump dei messaggi. A sua volta, il message pump potrebbe dare il controllo a qualcos’altro, e che qualcos’altro potrebbe richiedere del tempo. Il risultato è che l’operazione originale potrebbe richiedere molto più tempo per finire che se fosse in una discussione da sola che non produce mai controllo, decisamente più lungo del necessario.
      2. Duplicazione del lavoro. Poiché è ansible trovarsi a eseguire lo stesso metodo due volte e sappiamo già che questo metodo è costoso / di lunga durata (o non si avrebbe bisogno di DoEvents () in primo luogo), anche se si è tenuto conto di tutte le dipendenze esterne menzionate sopra quindi non ci sono effetti collaterali negativi, si potrebbe ancora finire per duplicare un sacco di lavoro.
      3. L’altro problema è la versione estrema del primo: un potenziale di stallo. Se qualcos’altro nel tuo programma dipende dal completamento del tuo processo, e bloccherà fino a quando non lo farà, e quella cosa viene chiamata dal message pump di DoEvents (), la tua app rimarrà bloccata e non risponderà. Questo può sembrare inverosimile, ma in pratica è sorprendentemente facile da fare accidentalmente, e gli arresti sono molto difficili da trovare e eseguire il debug in seguito. Questo è alla base di alcune delle app app sospese che potresti avere sperimentato sul tuo computer.
    • Problemi di usabilità. Questi sono effetti collaterali che derivano dal non correttamente conto degli altri pericoli. Non c’è niente di nuovo qui, purché tu guardi in altri posti in modo appropriato.

    Se puoi essere sicuro di aver tenuto conto di tutte queste cose, allora vai avanti. Ma in realtà, se DoEvents () è il primo posto in cui cerchi di risolvere i problemi di risposta / aggiornamento dell’interfaccia utente, probabilmente non stai tenendo conto di tutti questi problemi correttamente. Se non è il primo posto che si guarda, ci sono abbastanza altre opzioni che vorrei mettere in dubbio come è stato fatto a considerare DoEvents () a tutti.

    La realtà è che la maggior parte delle volte, almeno nel mondo .Net, un componente di BackgroundWorker è quasi altrettanto facile, almeno una volta che lo hai fatto una volta o due, e farà il lavoro in modo sicuro. Più recentemente, il pattern async / await o l’uso di un Task può essere molto più efficace e sicuro.

    Tornando ai giorni Windows a 16 bit, quando ogni attività condivideva un singolo thread, l’unico modo per mantenere un programma reattivo in un ciclo limitato era DoEvents . È questo uso non modale che è scoraggiato a favore dei thread. Ecco un esempio tipico:

     ' 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 

    Per cose modali (come tenere traccia di un popup), è probabile che sia ancora OK.

    Potrei sbagliarmi, ma mi sembra che DoDragDrop e TrackPopupMenu siano casi piuttosto speciali, in quanto prendono il controllo dell’interfaccia utente, quindi non hanno il problema della rientranza (che credo sia la ragione principale per cui le persone descrivono DoEvents come “Evil” ).

    Personalmente non penso sia utile liquidare una caratteristica come “Evil”, piuttosto spiegare le insidie ​​in modo che le persone possano decidere da sole. Nel caso di DoEvents ci sono rari casi in cui è ancora ragionevole utilizzarlo, ad esempio mentre viene visualizzata una finestra di dialogo di avanzamento modale, in cui l’utente non può interagire con il resto dell’interfaccia utente, quindi non c’è alcun problema di re-entrancy.

    Ovviamente, se per “Male” intendi “qualcosa che non dovresti usare senza comprendere appieno le insidie”, allora sono d’accordo che DoEvents sia malvagio.