Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Problem mit Delay (https://www.delphipraxis.net/41918-problem-mit-delay.html)

PaKir 10. Mär 2005 18:04


Problem mit Delay
 
Ich hab nach einer alternative zu “sleep“ und einer anderen Prozedure gesucht und das hier hier im Forum gefunden Delay revisited. Funktioniert auch eigentlich ganz gut, da die CPU-Auslastung angenehm niedrig bleibt.

Delphi-Quellcode:
procedure Delay(Milliseconds: Integer);
var
  Tick: DWord;
  Event: THandle;
begin
  Event := CreateEvent(nil, False, False, nil);
  try
    Tick := GetTickCount + DWord(Milliseconds);
    while (Milliseconds > 0) and
          (MsgWaitForMultipleObjects(1, Event, False, Milliseconds, QS_ALLINPUT) <> WAIT_TIMEOUT) do
    begin
      Application.ProcessMessages;
      Milliseconds := Tick - GetTickcount;
    end;
  finally
    CloseHandle(Event);
  end;
end;
Jetzt aber zu meinem Problem. Nach ein paar Sekunden kommt immer dieser Fehler bei "Milliseconds := Tick - GetTickcount":
http://img34.exs.cx/img34/3849/unbenannt4np.jpg
Ich hab wirklich überhaupt keine Ahnung warum. Im Normalbetrieb meines Programms ändert sich die Zeit zwar andauernd aber auch bei einem Testprogramm das immer 200msec warten soll kommt der Fehler. Wisst ihr an was das liegen kann.

mirage228 10. Mär 2005 18:17

Re: Problem mit Delay
 
Hi,

versuchs mal so, in dem Du den Typ von "Milliseconds" von Integer zu Int64 änderst.

mfG
mirage228

PaKir 10. Mär 2005 18:35

Re: Problem mit Delay
 
Habs geändert und jetzt gehts. :thumb:
Hätt ich auch selbst drauf kommen können. :oops: :duck: :oops:
Hab nur gedacht das das bei so kleinen Werten nicht an sowas liegen kann.

[EDIT]Geht doch nicht. Der Fehler kommt zwar nicht mehr so oft aber er kommt[/EDIT]

Luckie 10. Mär 2005 19:03

Re: Problem mit Delay
 
Zitat:

Zitat von PaKir
Habs geändert und jetzt gehts. :thumb:

Ob das die Lösung war, ist die Frage. Lass das Programm mal mehrere Stunden laufen. MSDN-Library durchsuchenGetTickCount liefert auch nur einen DWORD zurück. Ich sehe im Moment nichts, warum ein Int64 benötigt würde.

v2afrank 18. Feb 2008 09:16

Re: Problem mit Delay
 
Ich weiß zwar, dass der Thread hier ewig alt ist, abe gibt es dafür schon eine Lösung ?
Ich habe das Problem jetzt nämlich genauso. Interessanterweise taucht das Problem nur auf meinem XP Rechner auf. Unter Windows 2000 habe ich das Problem nicht

Xong 18. Feb 2008 09:20

Re: Problem mit Delay
 
Zitat:

Zitat von v2afrank
Ich weiß zwar, dass der Thread hier ewig alt ist, abe gibt es dafür schon eine Lösung ?
Ich habe das Problem jetzt nämlich genauso. Interessanterweise taucht das Problem nur auf meinem XP Rechner auf. Unter Windows 2000 habe ich das Problem nicht

Der gleiche Code?

v2afrank 18. Feb 2008 09:30

Re: Problem mit Delay
 
Ja, genau der gleiche Code. Als Delay habe ich 100 übergeben

shmia 18. Feb 2008 09:41

Re: Problem mit Delay
 
Delphi-Quellcode:
// Überlaufprüfung muss abgeschaltet werden:
{$Q-}
procedure Delay(Milliseconds: Integer);
...

v2afrank 18. Feb 2008 09:45

Re: Problem mit Delay
 
Ok,
danke für den Tipp werde ich machen. Aber was genau passiert denn da ?
Wenn das wirklich die Lösung ist ist sollte man dann nich auch den Beitrag in der Codelib ändern ?

Xong 18. Feb 2008 09:49

Re: Problem mit Delay
 
Delphi-Quellcode:
procedure Delay(Milliseconds: Cardinal);
var
  Tick: DWord;
  Event: THandle;
begin
  Event := CreateEvent(nil, False, False, nil);
  try
    Tick := GetTickCount + Milliseconds;
    while (Milliseconds > 0) and
          (MsgWaitForMultipleObjects(1, Event, False, Milliseconds, QS_ALLINPUT) <> WAIT_TIMEOUT) do
    begin
      Application.ProcessMessages;
      Milliseconds := Tick - GetTickcount;
    end;
  finally
    CloseHandle(Event);
  end;
end;
Ich kann es mir nur so erklären, dass GetTickCount einen vorzeichenlosen Integerwert zurückliefert, "Milliseconds" aber VZ-behaftet ist. Mit diesem Code müsste das Problem behoben sein.

v2afrank 18. Feb 2008 09:55

Re: Problem mit Delay
 
Das funktioniert nicht. Hatte ich auch schon ausprobiert. (while (Milliseconds > 0)) ist dann ja auch immer true.
Was (anscheinend) geholfen hatte, war folgende Zeile
Milliseconds := integer(Tick) - integer(GetTickcount);
Da ich aber nicht verstehe was da passiert, traue ich mich nicht das wirklich zu benutzen

Xong 18. Feb 2008 10:08

Re: Problem mit Delay
 
Zitat:

Zitat von v2afrank
Das funktioniert nicht. Hatte ich auch schon ausprobiert. (while (Milliseconds > 0)) ist dann ja auch immer true.

Du hast recht. Dann halt so (Ist sogar ´nen Tick schneller. =) ):
Delphi-Quellcode:
procedure Delay(Milliseconds: Cardinal);
var
  Tick: DWord;
  Event: THandle;
begin
  Event := CreateEvent(nil, False, False, nil);
  try
    Tick := GetTickCount + Milliseconds;
    while (Tick > GetTickcount) and
          (MsgWaitForMultipleObjects(1, Event, False, Milliseconds, QS_ALLINPUT) <> WAIT_TIMEOUT) do
    begin
      Application.ProcessMessages;
    end;
  finally
    CloseHandle(Event);
  end;
end;

Zitat:

Zitat von v2afrank
Was (anscheinend) geholfen hatte, war folgende Zeile
Milliseconds := integer(Tick) - integer(GetTickcount);
Da ich aber nicht verstehe was da passiert, traue ich mich nicht das wirklich zu benutzen

Ein ganz normaler Typcast. Irgendwie scheint es halt Probleme zu geben, wenn DWORD, Integer und Cardinal aufeinander prallen. Das Spiel mit den signed und unsigned Variablen sollte man also vorsichtig genießen.

Ich würde mich freuen, wenn du meine Lösung einmal testen würdest.

LG,
Xong

sirius 18. Feb 2008 10:24

Re: Problem mit Delay
 
@Xong: Dein letztes Beispiel ist aber falsch.
Du musst milliseconds schon aktualisieren.

Normalerweise hat man doch eh die Überlaufprüfung ausgeschaltet. die schaltet man doch nur mal kurz ein, um Fehler zu finden. In diesem Code stört ein Überlauf allerdings nicht.

Wie lange willst du eigentlich warten?

negaH 18. Feb 2008 10:33

Re: Problem mit Delay
 
Schalte die blöde Überlaufprüfung des Compilers ab, DIE IST die Fehlerursache nicht der Code noch Windows. Das Problem ist recht schnell erklärt. Wenn der programmierer im Source sagt -> mache eine Subtraktion ohne Vorzeichen und benutze dazu den vorzeichenlosen Datentyp Cardinal/DWord dann baut der Compiler bei der Überlaufprüfung eine Prüfung MIT Vorzeichen ein. Statt also einen Wertebereich von 0 bis 2^32-1 wird auf Bereich -2^31 bis +2^31-1 geprüft.

Ich betone es immer wieder: die Überlaufprüfung der Delphi Compiler wurde von Version zu Version (also ausgehend vom guten alten BP4), von einem Präzisionsinstrument immer mehr zu einem Schätzeisen und schlußendlich zu einer nervigen Zecke. Das Problem wurde zusätzlich verschärft durch die manchesmal falschen Datentypen. So wurde der Typ Cardinal schonmal als Integer also vorzeichenbehaftet durch den Compiler implementiert und das galt nicht nur für diesen Datentyp. Daraufhin mussten die Entwickler tricksen, gewöhnten sich teilweise dran. Eine Version später hatte Borland solche Bugs teilweise entfernt und dafür den neuen Datentyp Int64 fehlerhaft eingebaut und schnarch langsam. Auch in den Windows-API Schnittstellen gab es häufig solche Fehler, eben GetTickCount:Integer statt wie schon immer GetTickCount:Cardinal 32 Bit vorzeichenlos. Das ist nämlich immer schon ein 32Bit vorzeichenloser Rückgabewert, denn wer von Euch hat seinen Rechner schon -2^31 Millisekunden am Laufen bevor er diesen überhaupt eingeschaltet hat. Aber GetTickCount wurde eben lange Zeit mit Integer-Rückgabewert deklariert. Gleiches PerformanceCounter, am Anfang ein übles packed Record Konstrukt um einen Unsigned 64 Bit darzustellen, dann getrickst per Comp Fließkommatyp was ganz gut funktionmierte, dann ein Int64 also vorzeichenbehaftet obwohl man niemals den Rechner früher am Laufen haben kann bevor man ihn eingeschaltet hat und schlußendlich UInt64.
Nun das sind eben Entwicklungen in der Weiterentwicklung und Verbesserung eines Programmierwerkzeuges die ganz normal sind. Und wenn man nicht höllisch aufpasst dann verursachen solche Dinge schnell mal Überlauffehler usw. die eigentlich garkeine sind.

Bei Delay solltest du eben darauf achten das man nicht mit 2^31 Millisekunden Delays arbeitet, dafür habe ich diese Funktion garnicht erschaffen (naja davon abgesehen ist sie eh ein sehr gefährliches Instrument das ich ablehne weil es immer darauf hinausläuft das ein Programmierer der grundsätzlich an seinem Bedienkonzept was falsch macht auf Delay zurückgreifen muß, normalerweise braucht man niemals ein Delay() wenn man es richtig angeht).

Sollte es also bei Delay zu einem Überlauf kommen so ist das richtig und produziert laufzeittechnisch keinen Fehler, das habe ich schon berücksichtigt. Also selbst wenn GetTickCount in der ersten Abfrage einen Wert nahe 2^32 liefert so würde durch die Berechnung von Milliseconds := Tick - GetTickCount als inverse Operation dieser Überlauf wieder korregiert.

@Xong: dein Vorschlag verändert die geplante Funktionsweise des Originals. Milliseconds in WaitFor() ist ein releativer Wert und muß jedesmal neu errechnet werden da ansonsten die gesamte delay Funktion nicht nahezu exakt Milliseconds Millisekunden warten würde, sondern durchschnitlich betrachtet +Millisekunden länger.
Gruß Hagen

Xong 18. Feb 2008 10:40

Re: Problem mit Delay
 
Zitat:

Zitat von sirius
@Xong: Dein letztes Beispiel ist aber falsch.
Du musst milliseconds schon aktualisieren.

Du hast natürlich recht. Ich bin wohl heute etwas unkonzentriert.
Dafür habe ich eine(n) Erklärung(sversuch) für den Überlauf.

Delphi-Quellcode:
Milliseconds := Tick - GetTickcount
Irgendwann wird Tick kleiner GetTickcount. Da aber GetTickcount vorzeichenlos ist, nehme ich an, dass das Ergebnis von "Tick-GetTickcount" ebenfalls vorzeichenlos gespeichert wird. Es kommt also bei der Berechnung zum Überlauf, nicht bei der Zuweisung. Die Lösung ist einfach: Man muss die Operanden vorher in VZ-behaftete Variablen casten.

Alternativ kann man aber auch meinen Code nutzen (, welcher jetzt hoffentlich richtig ist. =) ):
Delphi-Quellcode:
procedure Delay(Milliseconds: Cardinal);
var
  Tick: Cardinal;
  Event: THandle;
begin
  Event := CreateEvent(nil, False, False, nil);
  try
    Tick := GetTickCount + Milliseconds;
    while (Tick > GetTickcount) and
          (MsgWaitForMultipleObjects(1, Event, False, Tick-GetTickCount, QS_ALLINPUT) <> WAIT_TIMEOUT) do
    begin
      Application.ProcessMessages;
    end;
  finally
    CloseHandle(Event);
  end;
end;

Xong 18. Feb 2008 10:44

Re: Problem mit Delay
 
Zitat:

Zitat von negaH
@Xong: dein Vorschlag verändert die geplante Funktionsweise des Originals. Milliseconds in WaitFor() ist ein releativer Wert und muß jedesmal neu errechnet werden da ansonsten die gesamte delay Funktion nicht nahezu exakt Milliseconds Millisekunden warten würde, sondern durchschnitlich betrachtet +Millisekunden länger.

Ja-ha!
Ich hab´s ja verstanden. :wall:
Immer druff auch mich. *am boden zerstört*

:angel2: :dp:

negaH 18. Feb 2008 10:49

Re: Problem mit Delay
 
Jain :) man könnte ihn so lassen da zwischen den beiden Aufrufen im While Kopf von Gettickcount() kein allzugroßer Fehler entstehen wird, allerdings ist er eben unellegant da man zweimal überflüssigerweise GetTickCount() aufruft. So wie meine Originalfunktion ist ist sie schon am besten, man muß halt Überlaufprüfungen abschalten, fertig. Man könnte eine Sache abändern, nämlich den Parameter Millisecinds als DWord statt Cardinal deklarieren, ist aber eine Schönheitskorretur und wird mit an Sicherheit grenzender Wahrscheinlichkeit dazu führen das diese Funktion nicht mehr in allen Delphiversionen ohne Fehler/Warnunung komplierbar ist. Eben auf grund der ständigen Änderungen am Compiler und Windowsimporten, Cardinal/DWord als Integer oder als 32Bit vorzeichenlos, GetTickCount mal mit Integer mal DWord (32Bit) Rückgabewert, WaitFor() Parametertypänderungen von Timeout usw. Die Kunst dabei war es also den Source möglichst so zu schreiben das er mit allen Delphiversionen relativ sauber läuft.


Wie gesagt: Die Benutzung von Delay() ist ein starkes Indiz dafür das der Programmierer generell irgendwas konzeptionell falsch macht. Windows/VCL basierte Anwendungen sollten immer ereignisbasiert arbeiten, statt auf Basis eines Pollings und das macht Delay() nämlich. Das Dumme dran ist nun die Forderung das das Anhalten der kompletten Anwendung, indem man die Veränderung ener Zeitspanne pollen möchte, nicht erwünscht ist. Also kommt man auf die eigentlich dummme Idee während diesem Polling noch Application.ProcessMessages aufzurufen. Einige denken sich: rufe ich doch Appplication.ProcessMessages permanent auf, also auch wieder Polling des Messagequeues und wundern sich dann das die CPU Auslastung auf 100% hochschnellt obwohl fast garnichts im System passiert. Andere versuchen es cleverer zu lösen, sie benutzen WaitForMultipleObjects() usw. und benutzen damit wenigstens teilweise ein ereignisbasiertes System. Sie vermeiden so wenigsten das unnötige Verbraten von Rechenzeit. Aber beide machen im Grunde einen Denkfehler denn sie handeln sich mit dem nun alternativen Pfad der Messageabarbeitung durch ein nicht mehr zentralisiertes Aufrufen von Application.ProcessMessages an anderer und erstmal unsichtbarer Stelle übelste Fehler ein. Dann kommt irgendwan eine Anfrage hier in der DP warum ihre Anwendung beim Beenden ständige Exceptions auslösst die final in einem RunTime Errror endet. Logisch: Anwendung ist in Delay() wird nun von User terminiert über Schließenbutton, MainForm wird zerstört befindet sich aber noch in Delay(), Delay() wird irgendwan mal fertig springt zurück in das nun zerstörte Form und schwups Exceptions. Und da diese Beendigung auch noch in der Finalizierungsphase stattfindet, also das Program in der Kette dr Unit-Finalization-Sektionen sich befindet, der Exceptionhandler da schon längst deinstalliert wurde, sieht man im besten Falle nur noch Runtime Errors. Und das Ganze liegt nur daran das man ein Delay() aufruft und darin über Application.ProcessMessages eine alternativen Messageabarbeitungspfad aufmacht.
Und was macht dieser Programmierer in seiner Verzweiflung -> er versucht mit TerminateProcess() oder TerminateThread(MainThread) das Problem zu "lösen" indem er radikal seine eigene Anwendung abschlachtet. Fehlerursache nicht gefunden aber Problem gelösst meint er, aus den Augen aus dem Sinn. Ändern tut es nichts an dem Fakt das dieser Programmierer rein konzeptionell garnicht programmieren kann !


Probiers aus: Nehme ein TForm + Button1 drauf. In Button1Click() dann eine Delay() das mehrere Minuten dauert. Vor den Aufruf dieses Delay() noch ein ShowMessage('ich warte'), dann Delay(Minutenlang) und danach ein ShowMessage(Self.Caption). Dann im Form.Ondestroy eine ShowMessage('ich zerstöre TForm'). So nun starten und auf Button Clicken. Danach das Formular schließen und sehen was passiert.

Gruß Hagen

v2afrank 18. Feb 2008 11:00

Re: Problem mit Delay
 
Also ich kann jetzt auch bestätigen, dass es mit abgeschalteter Überlaufüberprüfung nicht mehr zu dem Fehler kommt.
Tut mir leid, dass ich den Vorschlag von shmia nicht vorher testen konnte.
Sollte dann nicht trotzdem ein Hinweis in der Codelib auf die abzuschaltende Überlaufüberprüfung erfolgen ?

Xong 18. Feb 2008 11:04

Re: Problem mit Delay
 
Nimm das, Schurke!
Delphi-Quellcode:
procedure Delay(Milliseconds: Cardinal);
var
  Tick: Cardinal;
  Event: THandle;
begin
  Event := CreateEvent(nil, False, False, nil);
  try
    Tick := Milliseconds;
    // Milliseconds wird missbraucht als Zwischenspeicher für GetTickcount
    Milliseconds := GetTickcount;
    Tick := Tick + Milliseconds;

    while (Tick > Milliseconds) and
          (MsgWaitForMultipleObjects(1, Event, False, Tick-Milliseconds, QS_ALLINPUT) <> WAIT_TIMEOUT) do
    begin
      Application.ProcessMessages;
      Milliseconds := GetTickcount;
    end;
  finally
    CloseHandle(Event);
  end;
end;
Vorteil: Es wird nur mit VZ-losen Werten gerechnet.
Nachteil: Milliseconds wird schändlich missbraucht.

negaH 18. Feb 2008 11:10

Re: Problem mit Delay
 
Nö, wenn sollte ein Hinweis auf die NICHT zuschaltbare Überlaufpüfung kommen, denn standardmäßig ist sollte sie aus sein, Gründe siehe oben. Am besten ist es sie per Compilerswitches zu deaktivieren. Ich persönlich bevorzuge das nicht. Dh. ich möchte das wenn ich in den Projektoptionen mal testweise die Überlaufprüfung aktiviere ich auch in jeder Sourcezeile dann eine Überlaufprüfung drinnen habe, egal ob sie dort Sinn oder nicht macht. In einem Produktivcode sollte man sowieso immer diese Überprüfungen aus haben. Diese Prüfungen sind also nur Werkzeuge für den Entwickler und gehören niemals in ein fertiges Produkt rein.

Ich betone nochmal: überlege dir ob du Delay() wirklich benötigst, ob nicht ein TTimer besser wäre oder gleich das Konzept mal zu überdenken. Möchtest du Delay() benutzen musst du wissen was dann alles pasieren kann. Achte dann darauf das der Delay() Aufruf nicht reentrant sein darf, achte darauf das der Benutzer nicht die Anwendung bzw. das Form terminieren können darf, achte darauf das in der Delayphase keine anderen Dialoge, TForms usw. aufpoppen dürfen usw.

Gruß Hagen

v2afrank 18. Feb 2008 12:30

Re: Problem mit Delay
 
Du hast mich ja schon überzeugt. Ich habe hier einen Thread benutzt, der eine serielle Schnittstelle abpollt.
Ich bin gerade dabei das das ganze mit Hilfe der Cportlibary ereignisbasiert zu gestalten

sirius 18. Feb 2008 12:40

Re: Problem mit Delay
 
Zitat:

Zitat von v2afrank
Du hast mich ja schon überzeugt. Ich habe hier einen Thread benutzt, der eine serielle Schnittstelle abpollt.
Ich bin gerade dabei das das ganze mit Hilfe der Cportlibary ereignisbasiert zu gestalten

In einem Thread ist es tödlich auf Application zuzugreifen. Da solltest du schon auf deine eigene Messageverarbeitung im Thread zurückgreifen, was auch bei weitem sinnvoller ist. Und wenn im Thread keine Messages abgearbeitet werden, dann ist es durchaus legitim (im Thread) sleep zu benutzen. Aber mit Abstand besser ist es natürlich mit Ereignissen zu arbeiten.

negaH 18. Feb 2008 12:53

Re: Problem mit Delay
 
Wenn du in einem Thread warten möchtest dann benutze WaitForMultipleObjects(). In WFMO() speicherst du bei Events ein Array auf deine Eventhandles ab und als Timeout setzt du meistens INFINITE. So wird der Thread gezielt schlafen gelegt und Windows weckt diesen bei einem Event auf. Da die RS232 Schnittstellen, vom Windows-API aus betrachtet, sowieso Eventbasiert sind und Overlapped arbeiten können, ist diese Vorgehensweise die Beste. Neben den Events die du für die Overlapped-Results und RS232 benötigst (WaitCommEvent usw.) kannst du zusätzlich nun auch Events anlegen die du zb. extern ausserhalb des Threadobjektes auf sichere Weise auslösen kannst. Zb. ein Terminierungsevent beim Zerstören des Threadobjekts ist so ein Fall. Du signalisierst dort ein Event, zb. EventThreadTerminate das im Array der Events von WaitForMultipleObjects enthalten ist. Wartet der Thread nun an dieser Stelle so löst das signalisierte Event die Rückkehr aus dieser WFMO() Funktion aus. Als Resultat bekommst du vom Windows mitgeteilt das es das ThreadTerminateEvent, also der Index in das Events Array, was das signalisierte. In einer nachfolgenden CASE Abfrage dieses Resultates kannst du also dann gezielt das Event zurücksetzen und die Threadproc verlassen. Auf Mainthread-Seite wird nach dem signalisieren mit ThreadTerminateEvent selber nun mit MsgWaitForMultipleObjects() und dem TThread.Handle auf diesen gewartet. Also erst wenn der Thread auf ThreadTerminateEvent reagiert hat und seine ThreadProc verlassen hat kehrt die Prozedur im MainThread zurück. So hat man sauber und ohne Rechenzeitverschwendung einen Thread terminiert.

Gruß Hagen

EDIT: au mann, meine Tippfehler sind immer wieder nervend. Scheiß M$-Keyboard.

Landfloh 18. Feb 2008 13:20

Re: Problem mit Delay
 
Probiere mal sleep (...). In die Klammer die Millisecunden

mkinzler 18. Feb 2008 13:25

Re: Problem mit Delay
 
Vergesse Sleep() besser

bluesbear 18. Feb 2008 13:32

Re: Problem mit Delay
 
Zitat:

Zitat von negaH
(...)
Aber beide machen im Grunde einen Denkfehler denn sie handeln sich mit dem nun alternativen Pfad der Messageabarbeitung durch ein nicht mehr zentralisiertes Aufrufen von Application.ProcessMessages an anderer und erstmal unsichtbarer Stelle übelste Fehler ein. Dann kommt irgendwan eine Anfrage hier in der DP warum ihre Anwendung beim Beenden ständige Exceptions auslösst die final in einem RunTime Errror endet. (...)

Danke für diese nachvollziehbare Erläuterung! :-D

negaH 18. Feb 2008 14:52

Re: Problem mit Delay
 
@bluesbear:
Jo keine Ursache, ich als Borland Entwickler hätte Application.ProcessMessages und Konsorten von vornherein als private, nja vieleicht protected deklariert. Der Aufruf von Application.ProcessMessages an nicht zentralisierter Stelle, nämlich in Application.Run; ist immer gefährlich. Meistens möchte man nur mal die Darstellung des GUI's auffrischen, dann sollte mit TControl.Update gearbeitet werden, zb. TForm.Update oder RedrawWindow(Self.Handle, 0, RDW_UPADTENOW or RDW_ALLCHILDREN or RDW_INVALIDATE or RDW_NOERASEBKGND) oder mit einer eigenen Iteration über TForm.Controls[]. Alles andere, also zeitverzögerte Ausführungen dann per TTimer, soweit die grobe Auflösung von >= 20ms ausreichend ist. Braucht man es genauer bietet sich der Multimedia Timer an, Threads helfen kaum bei diesem Problem. Ein MMTimer hat aber erhöhte Anforderungen zb. das die aufgerufene Funktion nicht zu lange dauert und sich quasi "überschlägt" und wie eine Threadprocedure zu behandeln ist, sprich Vorsicht beim Zugriff auf globale Variablen und deren darin gespeicherten nicht-threadsicheren Objekten wie zb. TApplication. Threads selber sollten nach Möglichkeit keine Messagebearbeitungen machen. Das ist auch ein Sicherheitsrisiko, ähnlich wie Dienste/Services mit GUI.

Hält man sich nicht an solche Regeln dann bekommt man die bekannten Probleme die uns selber bei anderen Produkten ständig nerven. Man schiebt eine CD-ROM ein und greift dummerweise mit dem Windows-Explorer auf eine Netzwerkumgebung zu. Und bums blockieren sich beide blockierenden Aktionen gegenseitig, weil sie selber pollen. Denkt man das der Taskmanager das Problem beheben könnte irrt man sich meistens da auch dieser über das FileSystem geblockt wird und schwups hat man die miese Lage noch verschlimmbessert. Möchte man nun den Lock durch de CD-ROM Routinen beseitigen, indem man denkt man könnte das CR.ROM Fach hardwaremäßig per Schalter öffnen, so irrt man sich erneut. Entweder geht das garnicht weil der Windows-Explorer dies ebenfalls blockiert oder es geht und dafür streikt der Windows-Explorer noch mehr. Im schmlimmsten Fall fordert er einen auf die CR-ROM umgehend wieder einzulegen, damit die sich gegenseitig blockierende Situation auch ja wieder hergestellt wird. All das sind die Konsequenzen von einer asynchronen Verarbeitung bei der man über Blocking und Polling arbeitet.

Dann nerven diese Programme wie Kyodai Mahjong, die schönste DirectX Animationen benutzen und per Sleep() oder Delay() diese Animationen animieren. Das sich daraus eine 100% CPU Auslastung ergibt, mein Lüfter im Laptop quasi wie ein Hellikopter versucht gen Himmel zu fliegen ich das Ding an den Tisch nageln muß weil es sonst Richtung Küche fliegt und meine Bluetot-Strecke versagt, ist dem Programmierer nicht bewusst.

Dann diese Programme die eben mit einem alternativen Aufruf von Application.ProcessMessages wichtige Funktionen aus Appliction.Run deaktivieren. Das führt dann dazu das hinter dem Mainform dieser Anwendung, was natürlich sicherheitshalber vom Programmierer erstmal gesperrt wurde (DisableTaskWindows), ein Dialog hochpoppt, maybe eine Exception. Nun versuche mal an diesen Dialog ranzukommen wenn man ihn erstens nicht sieht und zweitens das davorliegende disabled Form nicht verschieben kann. Da der Dialog selber aber auch einen alternativen Application.ProcessMessage Pfad aufmacht -> nennt sich modale Dialogfunktion, blockiert sich nun das Delay() mit dieser Dialogfunktion. Gibts nur noch den Geheimtrick mit der ESCAPE Taste falls der Programmierer so schlau war und keinen Superduper Spezial Dialog ohne Titelleiste und somit Systemmenu erzeugt hat, und man noch die Zeit hat bis irgendeine andere Anwendung den Fokus vom Dialog zieht.

Ach ich hab wirklich Microsoft lieb gewonnen als es die Funktion in der Taskleiste vorgesehen hat das man beim mehrmaligen Schließen einer Anwendung diese abspachteln kann.

Gruß Hagen

shmia 19. Feb 2008 15:20

Re: Problem mit Delay
 
Zitat:

Zitat von Xong
Nimm das, Schurke!
Delphi-Quellcode:
procedure Delay(Milliseconds: Cardinal);
var
  Tick: Cardinal;
  Event: THandle;
begin
  Event := CreateEvent(nil, False, False, nil);
  try
    Tick := Milliseconds;
    // Milliseconds wird missbraucht als Zwischenspeicher für GetTickcount
    Milliseconds := GetTickcount;
    Tick := Tick + Milliseconds;

    while (Tick > Milliseconds) and
          (MsgWaitForMultipleObjects(1, Event, False, Tick-Milliseconds, QS_ALLINPUT) <> WAIT_TIMEOUT) do
    begin
      Application.ProcessMessages;
      Milliseconds := GetTickcount;
    end;
  finally
    CloseHandle(Event);
  end;
end;
Vorteil: Es wird nur mit VZ-losen Werten gerechnet.

Achtung da versteckt sich ein gemeiner Bug!!!
Angenommen GetTickCount liefert $FFFFF00. Es soll 1000 ms gewartet werden.
Also ist der Zielwert = $FFFFF00 + 1000 = (Taschenrechner her!) $000002E8
Nun wird die Bedingung in der While-Schleife die nächsten ~ 49 Tage nicht mehr True werden.
Die Wahrscheinlichkeit ist gering, dass dieser Bug auftritt.
Falls aber doch hängt das Programm.

negaH 19. Feb 2008 16:53

Re: Problem mit Delay
 
gibt es diesen Bug auch mit mener Funktion, gleich am Anfang dieses Threads ?

Gruß Hagen


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:38 Uhr.

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