AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

Problem mit Delay

Ein Thema von PaKir · begonnen am 10. Mär 2005 · letzter Beitrag vom 19. Feb 2008
Antwort Antwort
Seite 2 von 3     12 3   
v2afrank

Registriert seit: 9. Mai 2005
Ort: Bocholt
542 Beiträge
 
Delphi XE2 Professional
 
#11

Re: Problem mit Delay

  Alt 18. Feb 2008, 10:55
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
  Mit Zitat antworten Zitat
Benutzerbild von Xong
Xong

Registriert seit: 9. Jan 2008
186 Beiträge
 
Delphi 2006 Professional
 
#12

Re: Problem mit Delay

  Alt 18. Feb 2008, 11:08
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 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
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#13

Re: Problem mit Delay

  Alt 18. Feb 2008, 11:24
@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?
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#14

Re: Problem mit Delay

  Alt 18. Feb 2008, 11:33
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
  Mit Zitat antworten Zitat
Benutzerbild von Xong
Xong

Registriert seit: 9. Jan 2008
186 Beiträge
 
Delphi 2006 Professional
 
#15

Re: Problem mit Delay

  Alt 18. Feb 2008, 11:40
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.

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;
  Mit Zitat antworten Zitat
Benutzerbild von Xong
Xong

Registriert seit: 9. Jan 2008
186 Beiträge
 
Delphi 2006 Professional
 
#16

Re: Problem mit Delay

  Alt 18. Feb 2008, 11:44
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.
Immer druff auch mich. *am boden zerstört*

  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#17

Re: Problem mit Delay

  Alt 18. Feb 2008, 11:49
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
  Mit Zitat antworten Zitat
v2afrank

Registriert seit: 9. Mai 2005
Ort: Bocholt
542 Beiträge
 
Delphi XE2 Professional
 
#18

Re: Problem mit Delay

  Alt 18. Feb 2008, 12:00
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 ?
  Mit Zitat antworten Zitat
Benutzerbild von Xong
Xong

Registriert seit: 9. Jan 2008
186 Beiträge
 
Delphi 2006 Professional
 
#19

Re: Problem mit Delay

  Alt 18. Feb 2008, 12:04
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.
  Mit Zitat antworten Zitat
Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#20

Re: Problem mit Delay

  Alt 18. Feb 2008, 12:10
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
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 09:27 Uhr.
Powered by vBulletin® Copyright ©2000 - 2022, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf