Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung? (https://www.delphipraxis.net/174269-application-hang-anwendung-reagiert-nicht-bei-laengeren-operationen-loesung.html)

DSCHUCH 12. Apr 2013 22:54

Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,

ich habe jetzt mal noch ein wenig mit dem Problem experimentiert, das Windows immer meldet "Anwendung reagiert nicht mehr" sobald eine Operation länger als ein 3 Sekunden braucht (zumindest, wenn der Anwender noch einmal rumklickt).

Die Variante sämtliche längeren Operationen in Threads auszulagern finde ich ja von vorn herein absolut blödsinnig.

Die Erkenntnis: ich habe eine sehr sehr einfach Lösung gefunden.

In Kurz:

Delphi-Quellcode:
PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);//Refresh Application
dies zeichnet alles neu und hält die Anwendung am Leben, Nutzereingaben werden nicht bearbeitet. Ist im MSDN auch genauso beschrieben.
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx

Delphi-Quellcode:
while PeekMessage(Msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE) do;
while PeekMessage(Msg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE) do;
entfernt sämtliche Nutzereingabe, wodurch verhindert wird, das am Ende einer Schleife alles bearbeitet wird, do der Nutzer während der Wartezeit dumm rumgeklickt hat.

Damit ist eigentlich klar, das anstatt einem Application.ProcessMessages zum Refresh der Anwendung die obigen Befehle aufgerufen werden sollten.

Ich habe somit eine Funktion geschrieben:

Delphi-Quellcode:
procedure TForm11.ApplicationRefresh(RemoveUserInput: Boolean);
var Msg : TMsg;
begin
  PeekMessage(Msg, 0, 0, 0, PM_NOREMOVE);//Refresh Application
  if RemoveUserInput then begin
     while PeekMessage(Msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE) do
           ;//User Mouse Removed
     while PeekMessage(Msg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE) do
           ;//User Key Removed
  end;
end;
Im Anhang eine ganz einfache Testanwendung. Was haltet Ihr davon?

Aphton 12. Apr 2013 23:34

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Jo, interessant!

Nenns KeepAlive / Unfreeze das kann man nun als eine andere Option zu Delay sehen ^^
Soetwas müsste man direkt in die oberste Basisklasse für sichtbare Controls packen damit man dann ganz einfach Self/Form1.KeepAlive/Unfreeze aufrufen kann.

DSCHUCH 12. Apr 2013 23:41

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Vor allem macht dies auch automatisch die notwendigen Zeichenoperationen (auch so im MSDN beschrieben).

Letzlich wird ja nicht einmal etwas verändert: die Nachrichtenwarteschlange der Anwendung bleibt, wie sie ist. Die Anwendung arbeitet anschliessend ganz normal weiter. Sämtliche Timer etc verhalten sich ganz normal.

Einzig Windows wird dadurch benachrichtigt, das die Anwendung kontrolliert die Nachrichtenwarteschlange abarbeitet.

Aphton 12. Apr 2013 23:46

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Ich habs mal ein bisschen umgeschrieben (Class Helper)

Delphi-Quellcode:
type
  // Control-F1: "Controls.TControl is the base class for all components that are visible at runtime."
  // .. und weil nur mit sichtbaren controls interagiert werden kann (onclick, ...), wirds frühestens bei TControl benötigt
  TControlKeepAliveHlp = class helper for TControl
  public
    procedure KeepAlive(const DropUserInput: Boolean = True);
  end;

procedure TControlKeepAliveHlp.KeepAlive(const DropUserInput: Boolean); // "DSCHUSCH"
var
  DummyMsg: TMsg;
begin
  PeekMessage(DummyMsg, 0, 0, 0, PM_NOREMOVE);
  if DropUserInput then
  begin
    while PeekMessage(DummyMsg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE) do;
    while PeekMessage(DummyMsg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE) do;
  end;
end;

Bummi 13. Apr 2013 00:13

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
@Aphton ich sehe gerade nicht warum das ganze an einem Control hängen soll, es wird doch die Messagepipe mit 0, also für alle Handles abgefragt und (teil)geleert.

Aphton 13. Apr 2013 00:43

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Hmm..
Schlag was was besseres vor.

Das ist das, worauf ich auf die schnelle gekommen bin. Ich dachte mir, dass man, wenn man schon auf so Sachen wie delay() und eben dann keepAlive() ausweicht, schlechtes Design anwendet (GUI/Logik Trennung) und wenn man schon Dirty-Dinge macht - also in der GUI (sichtbare Controls -> Basisklasse Control) Berechnungen durchführt, dann kann man ja direkt hier mit keepAlive() ansetzen.

:stupid:

BUG 15. Apr 2013 20:37

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Zitat:

Zitat von Aphton (Beitrag 1211331)
schlechtes Design anwendet (GUI/Logik Trennung) und wenn man schon Dirty-Dinge macht

Das mit der Trennung kriegt man auch noch hin. Man könnte ApplicationRefresh/keepAlive ja auch in einem Callback (á la onProgress) aufrufen.


Zitat:

Zitat von Aphton (Beitrag 1211331)
Schlag was was besseres vor.

Delphi-Quellcode:
procedure keepAlive(const DropUserInput: Boolean); // "DSCHUSCH"
var
  DummyMsg: TMsg;
begin
  PeekMessage(DummyMsg, 0, 0, 0, PM_NOREMOVE);
  if DropUserInput then
  begin
    while PeekMessage(DummyMsg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE) do;
    while PeekMessage(DummyMsg, 0, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE) do;
  end;
end;
:mrgreen:


@DSCHUSCH: Coole Lösung :thumb:

DSCHUCH 15. Apr 2013 21:35

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Blumen auch an Himitsu, er hat die Grundidee erarbeitet. 8-)

Ja - so habe ich das jetzt auch testhalber in Verwendung - callbacks, welche die Application am Leben halten, ohne das man sonstwas bauen muß. Die Anwendung ist geblockt aber reagiert für Windows (kein Hang), Nutzereingaben werden ignoriert, genauso wie es sein muß. Das alles ohne Disable/Enable und wtf ever für einen blödsinnigen, aufwendigen Nudelcode, der sowieso nur zu Nebeneffekten führt. (zB da durch Application.ProcessMessages ja auch Timer aufgerufen werden können).

Funktionieren tut das aber auch nur aus dem Hauptthread heraus. Die Anwendung muß also nach wie vor vernünftig designed sein.

Achtung - beim Testen im Debugger verhält sich das anders, als wenn die Anwendung ohne Debugger läuft - Delphi hält die Debug-Anwendung hier sicher selbst am Leben, damit Windows nicht meckert "Anwendung reagiert nicht".

DSCHUCH 15. Apr 2013 22:36

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Kann dies mal jemand auf Windows Vista/XP/8 probieren? Die Testanwendung bietet ja die Option mit dem Radiobutton die Funktion an- und abzuschalten.

anro78 16. Apr 2013 07:55

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Zitat:

Die Variante sämtliche längeren Operationen in Threads auszulagern finde ich ja von vorn herein absolut blödsinnig.
Aber genau das macht man, wenn man eine Anwendung robust entwickeln will, alles andere ist doch nur Herumdoktern an den Symptomen. Was machst du denn z.B. bei einer SQL-Abfrage, die einige Sekunden dauert? Dann hast du gar keine Möglichkeit ApplicationRefresh aufzurufen! Die Implementierung von Threads zur Ausführung von Abarbeitungs-Jobs außerhalb des VCL-Mainthreads ist nun wirklich kein Teufelszeug und innerhalb weniger Minuten implementiert. Man hat die Möglichkeit, den Nutzer über eine Statusanimation entweder definiert (Progress) oder undefiniert (Marquee) über den Fortschritt zu informieren, die Anwendung bleibt weiterhin reaktionsfähig (kann z.B. minimiert oder das Fenster verschoben werden). Und eine Funktion, die sämtliche Top-Level-Controls in einem Fenster während der Abarbeitung sperrt ist auch schnell geschrieben.

musicman56 16. Apr 2013 08:08

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Zitat:

Zitat von DSCHUCH (Beitrag 1211598)
Kann dies mal jemand auf Windows Vista/XP/8 probieren?

Unter XP, Vista und Win8 in der VM probiert, keine Auffälligkeiten gefunden.

Anmerkung: Wenn ich ein "echter" User wäre, dann würde ich wahrscheinlich versuchen das Fenster zu verschieben (was nicht geht, und wünschenswert wäre) oder es zu minimieren (was nach Ablauf der Schleife passiert und eigentlich auch nicht passieren sollte).

SebastianZ 16. Apr 2013 08:56

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Verstehe ich was falsch, oder spricht irgend was gegen Application.ProcessMessages?

musicman56 16. Apr 2013 09:34

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Zitat:

Zitat von SebastianZ (Beitrag 1211617)
Verstehe ich was falsch, oder spricht irgend was gegen Application.ProcessMessages?

Die Spezialisten hier können das sicher besser erklären und begründen, aber ich versuch's mal: Wenn die App nur ein einzelnes Fenster hat, ist Application.ProcessMessages kein Problem. Sobald aber ein weiteres Fenster hinzukommt, im ungünstigsten Fall ein modaler Dialog, "lebt" das aufrufende Fenster bei einem Application.ProcessMessages weiter, und dann kann es zu unerwünschten Nebeneffekten kommen. Gerade bei modalen Fenstern geht man ja davon aus, dass der Programmablauf erst fortgesetzt wird, wenn das modale Fenster beendet wird.

Was anro78 vorgeschlagen hat, wäre aus meiner Sicht darum die elegantere Lösung. Ich bin Autodidakt und habe mangels dem entsprechendem Wissen mit Threads bisher überhaupt nicht gearbeitet, aber irgendwann werd ich mir das auch noch reinziehen :thumb:

BTW: Bin schon so oft über Application.ProcessMessages gestolpert, und darum interessiert mich auch dieser Thread.

DSCHUCH 16. Apr 2013 10:34

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Application.ProcessMessages kann zu rekursiven Aufrufen von Clickereignissen können, da Application.ProcessMessages den aktuellen Code unterbricht und Ereignisse neu verarbeitet.

Hast Du nur ein Form und eine Schleife mit 10000 Durchläufen, darin ein Application.ProcessMessages, kann der Nutzer den Button drücken, obwohl die Schleife noch nicht durchlaufen wurde. Die Schleife wird dann "Rekursiv" neu aufgerufen und idR hat man Access Violations.

Es ist eigentlich auch nicht möglich alles in threads zu packen, da man zB auch externe Komponenten hat, welche nicht Threadsave sind, welche aber wiederrum ein "OnProgress" anbieten.

Alle Forms Disablen und Enablen funktioniert auch nicht, da dann zB Timer feuern und Modale Fenster zerschossen werden.

anro78 17. Apr 2013 09:08

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Es ist schon möglich, alles in Threads zu packen. Das Zauberwort ist Schichtentrennung, Logik muss vom GUI getrennt werden. Zugegeben, Delphi macht das einem nicht leicht, aber bei einem robustem Programm führt kein Weg daran vorbei.

Bei der Verwendung von Threads muss alles was auf die VCL zugreift über den Aufruf von Synchronize synchronisiert werden, so auch die Anzeige von Progress usw.

Datenquellen auf denen im Thread gearbeitet wird, müssen entweder vorher abgebunden bzw. diabled werden.

Und von der Verwendung von Timern würde ich mal komplett abraten, das ist einfach nur Mist und sollte wirklich nur in Ausnahmefällen eingesetzt werden.

Mavarik 17. Apr 2013 10:35

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Hi!

Wenn Du eine Schleife von 1 bis x laufen lässt auf einen Button Click dann immer erst den Button im Clickevent disablen und
Application.Processmessages aufrufen, damit das auch ausgeführt wird.

Das Problem ist eher ein MYSQLQuery1.Execute der 2 Minuten dauert...

Threads sind aber auch kein "Allheilmittel" da durch die eingeschränkte Nutzung der VCL über Sycronize der Application Thread
alle I/O's machen muss... Dadurch bleibt der Thread z.B. stehen, wenn das Fesnter mit der Maus verschoben wird und zwar so lange bis
der User den Mausbutton wieder los läßt...

Eine alternative ist das auslagern in eine DLL, da eine DLL wieder einen eigenen VCL-0-Thread hat...

Mavarik

Elvis 17. Apr 2013 10:49

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Dafür ist ja auch TThread.Queue da. Damit hängt man etwas an den VCL Thread, ohne den eigenen Thread zu blockieren.

Mavarik 17. Apr 2013 11:55

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Zitat:

Zitat von Elvis (Beitrag 1211750)
Dafür ist ja auch TThread.Queue da. Damit hängt man etwas an den VCL Thread, ohne den eigenen Thread zu blockieren.

Das hilft aber nur, wenn der Thread nicht auf der Ergebnis Sync-Routine angewiesen ist...

anro78 17. Apr 2013 12:01

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Zitat:

Zitat von Mavarik (Beitrag 1211745)
Threads sind aber auch kein "Allheilmittel" da durch die eingeschränkte Nutzung der VCL über Sycronize der Application Thread
alle I/O's machen muss... Dadurch bleibt der Thread z.B. stehen, wenn das Fesnter mit der Maus verschoben wird und zwar so lange bis
der User den Mausbutton wieder los läßt...

Das ist doch nicht richtig. Wenn korrekt mit dem VCL-Thread synchronisiert wird, dann bleibt da nichts stehen, selbst eine eventuell synchronisierte ProgressBar läuft weiter.

Mavarik 17. Apr 2013 12:11

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Zitat:

Zitat von anro78 (Beitrag 1211763)
Zitat:

Zitat von Mavarik (Beitrag 1211745)
Threads sind aber auch kein "Allheilmittel" da durch die eingeschränkte Nutzung der VCL über Sycronize der Application Thread
alle I/O's machen muss... Dadurch bleibt der Thread z.B. stehen, wenn das Fesnter mit der Maus verschoben wird und zwar so lange bis
der User den Mausbutton wieder los läßt...

Das ist doch nicht richtig. Wenn korrekt mit dem VCL-Thread synchronisiert wird, dann bleibt da nichts stehen, selbst eine eventuell synchronisierte ProgressBar läuft weiter.

Ähh... Nein... Wenn Du Dein Fenster mit der Maus nimmst... und verschiebst... Bleibt alles stehen.

anro78 18. Apr 2013 07:14

AW: Application - Hang (Anwendung Reagiert nicht) bei längeren Operationen: Lösung?
 
Zitat:

Zitat von Mavarik (Beitrag 1211765)
Zitat:

Zitat von anro78 (Beitrag 1211763)
Zitat:

Zitat von Mavarik (Beitrag 1211745)
Threads sind aber auch kein "Allheilmittel" da durch die eingeschränkte Nutzung der VCL über Sycronize der Application Thread
alle I/O's machen muss... Dadurch bleibt der Thread z.B. stehen, wenn das Fesnter mit der Maus verschoben wird und zwar so lange bis
der User den Mausbutton wieder los läßt...

Das ist doch nicht richtig. Wenn korrekt mit dem VCL-Thread synchronisiert wird, dann bleibt da nichts stehen, selbst eine eventuell synchronisierte ProgressBar läuft weiter.

Ähh... Nein... Wenn Du Dein Fenster mit der Maus nimmst... und verschiebst... Bleibt alles stehen.

Das stimmt nicht, ich hab's hier in unserer Software ja vor mir auf dem Bildschirm: Ich kann von Beginn an der Stapelverarbeitung bis zum Ende die Maustaste gedrückt halten und das Fenster verschieben und zuschauen, wie der Stapel abgearbeitet wird und die ProgressBar sich füllt. Wenn das bei dir nicht so ist, dann machst du etwas falsch.


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:40 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz