Ich würde es vorziehen, zu sehen, wenn es einen vernünftigen Weg, dies zu behandeln, ohne einen zweiten Thread aufgrund der Besonderheiten dieser Anwendung (ursprünglich eine DOS-Anwendung mit vielen globalen statischen Variablen, konvertiert zu Windows/MFC, aber nicht von Grund auf entworfen, um Multi-Thread sein - nur machen es mehrere Dokumente bewusst war ein großes Unternehmen).
Die fragliche Anwendung versucht, eine sehr rechenintensive Operation durchzuführen, die den Nebeneffekt hat, dass das aktuelle Dokument geändert wird. Sie möchte in der Lage sein, das Hauptfenster und die Dokumentfenster iterativ zu aktualisieren, während der Prozess durchgeführt wird.
Der einfache Ansatz: Schleife durch die Arbeitselemente, Zeichnen auf dem Dokumentfenster, Ändern der zugrunde liegenden Daten des Dokuments und Aktualisieren der Statusleiste mit einem Statustext, der angibt, dass x von y abgeschlossen ist, funktioniert normalerweise. Aber manchmal hält die Anwendung die Aktualisierung der Statusleiste und des Dokumentfensters (Ansicht) an, bis der gesamte Auftrag abgeschlossen ist, und dann wird alles auf einmal aktualisiert.
Es gibt also keine Hänge-Bedingungen im Code, d.h. er wird nie nicht abgeschlossen. Es geht lediglich darum, dass Fenstermeldungen während der gesamten Dauer nicht verarbeitet werden (da es sich größtenteils um eine Anwendung mit nur einem Thread handelt).
Ich dachte, der offensichtliche Ansatz wäre, eine PeekMessage()-Schleife innerhalb der Task-Schleife zu platzieren, um alle gesammelten Nachrichten zu versenden. Ich nahm an, dass dies der Grund dafür ist, dass die visuellen Aktualisierungen nicht mehr stattfinden: Es muss eine Windows-Nachricht entweder in der Nachrichtenwarteschlange der Ansicht, des Mainframes oder des Threads sein, die die direkten Aktualisierungen des Bildschirms blockiert.
Vielleicht liegt das Problem aber auch woanders?
Unabhängig davon scheint es eine schlechte Idee zu sein, dass unsere Anwendung die Nachrichtenwarteschlange ignoriert, da dies Äonen von Verarbeitungszeit in Anspruch nehmen kann (der Benutzer wird die Anwendung als "nicht mehr reagierend" wahrnehmen, wenn er den Task-Manager fragt, nur weil unsere Nachrichtenwarteschlange nicht verarbeitet wird).
Die folgende Schleife wird jedoch unendlich:
// dispatch the messages until we're out of them again...
for (MSG msg; PeekMessage(&msg, NULL, 0, 0, PM_REMOVE); )
{
TraceMessage(msg.message, msg.wParam, msg.lParam);
if (msg.message == WM_QUIT)
return;
}
HINWEIS: TraceMessage() gibt einfach einen menschenfreundlichen Trace in das Debugger-Fenster aus, um welche Nachricht und Argumente es sich handelt. In diesem Fall ist es WM_PAINT.
Die Farbmeldung scheint also ewig in der Warteschlange zu verweilen (oder es werden unendlich viele neue Meldungen generiert), obwohl sie eigentlich entfernt werden sollte.
Fragen:
-
Könnte es sein, dass ich mich irre und der Grund, warum unsere Anwendungen die Statusleiste und die Ansicht nicht mehr aktualisieren können, ein anderer ist?
-
Gibt es einen besseren Ansatz für eine lange CPU oder Festplatte I/o-intensive Operation als Platzierung einer PeekMessage-Schleife innerhalb der Task-Schleife (die nicht die gesamte Anwendung neu zu architektieren, um mehr Multi-Thread-freundlich sein)?
Die Lösung, die ich anstrebe...
void DoSomethingLengthy()
{
CWaitCursor wait;
// disable our application windows much as if we were running a modal dialog
// in order to lock out the user from interacting with us until we're doing doing this thing
AfxGetMainWnd()->BeginModalState();
while (bMoreWorkToDo)
{
// empty any generated / received thread & window messages
for (MSG msg; PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE|PM_NOYIELD); )
AfxPumpMessage();
// for some reason, our cursor reverts to the normal pointer
// so force ourselves to continue to have the wait-cursor until we're really done
AfxGetMainWnd()->RestoreWaitCursor();
// here's where we do one work-item
// ...
}
// restore our prior state
AfxGetMainWnd()->EndModalState();
}
Ja, das ist eine sehr alte Technologie und nicht unbedingt der beste Ansatz. Sie ist jedoch machbar und für diesen Kontext sehr nützlich. Wir haben bereits eine komplexe Anwendung, die die OnIdle-Mechanik für eine Vielzahl von Zwecken nutzt, was sie als möglichen Ansatz weniger attraktiv macht.