Komische Probleme mit (Endlos)Schleife

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
ppahl
Beiträge: 56
Registriert: Fr 25. Nov 2016, 00:02

Komische Probleme mit (Endlos)Schleife

Beitrag von ppahl »

Gegeben: Lazarus 1.7 auf einem RPi; Im Programm eine Form mit einem Button und zwei Label. Desweiteren wird die Pascalio-Lib genutzt um Pins abzufragen. Läuft alles mit root-Rechten.
Folgende Prozedur als OnClick-Event des Buttons spinnt:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
begin
if dclk.value = false then label1.Caption := 'false' else label1.Caption := 'true';
  While Dclk.Value = false do
    begin
      label2.caption := 'Running';
    end;
end;
 


Bekloppter Code, ich weiss. Ist auch nur eine Simplifizierung um das Problem darzustellen: Solange Dclk.Value = false ist passiert gar nichts, erst wenn er auf true wechselt (dafür lege ich den Pin kurz auf Vcc, die GPIO-Auswertung funzt also) werden beide Label-caption-Befehle ausgeführt (also auch der vor der Schleife, mit der Anzeige 'false' natürlich da das der Status zum Zeitpunkt des Label.caption Befehls war). Ach ja, solange der Pin nicht auf true wechselt bleibt der Button nach dem Klick grau als ob er gerade gedrückt würde, das Progamm friert komplett ein und kann nur abgeschossen werden.
Irgendeine Idee was da schief läuft?

Und noch eine weitere Frage hinterher: Ich würde gerne eine Art Watchdog programmieren der die Schleife nach einer Zeit X abbricht.
Von AVRs bin ich es gewohnt einen Timer zu starten dessen ISR nach eben dieser Zeit ein Flag setzt welches dann in der Schleife ausgewertet wird und diese abbricht.
Mein Versuch: 'Flag' wird im FormCreate auf '0' gesetzt, in der Timer-ISR wird 'Flag' dann auf 1 geändert. Im Objektinspektor ist Timer1 disabled.

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
begin
if dclk.value = false then label1.Caption := 'false' else label1.Caption := 'true';
timer1.enabled := true;
   While Dclk.Value = false do
    begin
      if Flag = 1 then break;
      label2.caption := 'Running';
    end;
end;
 


Funktioniert nicht, auch die Label werden nicht angezeigt.
Aber jetzt wirds schräg: Wenn ich im Objektinspektor den Timer direkt enable dann funktionierts, inklusive Anzeige des _ersten_ Labels...aber nur dann wenn ich den Button erst _nach_ Ablauf der Timerzeit klicke. D.h. das Flag wird dann auch innerhalb der Schleife ausgewertet und das _erste_ Label angezeigt - das zweite aber wiederum nicht! Die while...do Schleife blockiert also auch den Timer so dass er das Flag nicht setzen kann, er muss schon vor Start der Schleife das Flag gesetzt haben. Dann wird auch die Schleife verlassen und das Programm kann auch normal beendet werden...nur der zweite Label-Befehl wird nicht ausgeführt, obwohl die Schleife mit diesem Befehl ja x-mal durchlaufen worden sein muss. Warum ich das erwähne: Vertausche ich die Reihenfolge der Flagauswertung und des Label-Befehls...

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
begin
if dclk.value = false then label1.Caption := 'false' else label1.Caption := 'true';
timer1.enabled := true;
   While Dclk.Value = false do
    begin
      label2.caption := 'Running';
      if Flag = 1 then break;
    end;
end;
 


... dann wird das zweite Label auch angezeigt.

Ich stehe irgendwie total auf dem Schlauch...

creed steiger
Beiträge: 957
Registriert: Mo 11. Sep 2006, 22:56

Re: Komische Probleme mit (Endlos)Schleife

Beitrag von creed steiger »

While Dclk.Value = false do
begin
label2.caption := 'Running';
end;

solange falsch renne im Kreis und schreib "Renne" auf Label2

leider wird in diesem Moment auch wirklich nichts anderes gemacht,
also auch nicht deine Labels/Formular neugezeichnet

du kannst ein

application.ProcessMessages;
um alle Nachrichten abzuarbeiten
in die while Schleife einfügen

aber für deinen blockierenden Ansatz würde ich eher Threads und synchronize empfehlen.

Eine Endlosschleife in einen Timer zu packen finde ich völlig sinnfrei, was willst du damit erreichen?
Der Timer läuft doch schon periodisch.

im ontimer Ereignis kannst du die Zustände doch einfach abfragen und deinen Labels zuweisen.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Komische Probleme mit (Endlos)Schleife

Beitrag von mschnell »

In einer Lazarus-Applikation: Endlos = sinnlos, also verboten !!! :)

Dafür gibt es "Events".

-Michael

ppahl
Beiträge: 56
Registriert: Fr 25. Nov 2016, 00:02

Re: Komische Probleme mit (Endlos)Schleife

Beitrag von ppahl »

creed steiger hat geschrieben:solange falsch renne im Kreis und schreib "Renne" auf Label2

leider wird in diesem Moment auch wirklich nichts anderes gemacht,
also auch nicht deine Labels/Formular neugezeichnet

Nun, wenn nur das Label innerhalb der Schleife nicht neugezeichnet würde wäre das ja das eine, aber es wird ja auch das Label _vor_ Eintritt in die Schleife nicht neugezeichnet. Vorauseilender Gehorsam?
Und das Beispiel ist wie gesagt ja aufs Einfachste heruntergebrochen, genauso könnte auch ein Zähler von 1 bis 1Mio abgearbeitet werden und in der Schleife dann irgendwelche Berechnungen abgearbeitet werden : Auch dann könnten die Ergebnisse nicht dargestellt werden bis die Schleife komplett abgearbeitet und beendet ist?

creed steiger hat geschrieben:Eine Endlosschleife in einen Timer zu packen finde ich völlig sinnfrei, was willst du damit erreichen?
Der Timer läuft doch schon periodisch.

Wieso Schleife im Timer? Vor Start der Schleife wird ein Timer gestartet, dessen OnTimer-Prozedur ändert eine Variable. Und diese Variable wird in der Schleife abgefragt, der Timer selbst kommt in der Schleife gar nicht vor. Die Frage ist nun wieso die Schleife das Programm komplett blockiert, also der Timer offensichtlich nicht hochtickert, das Flag nicht setzen kann und daher die Schleife auch nicht beendet wird? Der Witz so eines Timers ist doch eigentlich dass er parallel weiterläuft.
Ich habe auch einen zweiten Button auf die Form gelegt und in dessen OnClick-Event das Flag gesetzt, genau das Gleiche: Wird nicht ausgeführt.
Wie schon beschrieben: Normalerweise ist ein Button hell. Klickt man drauf wird er grau. Lässt man die Maustaste wieder los wird er wieder hell. Hier aber nicht, er bleibt grau, das Programm hängt sich komplett weg. Das kann doch aber nicht sein, auch während der Abarbeitung von Schleifen sollten Events - welcher Art auch immer - doch behandelt werden?

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Komische Probleme mit (Endlos)Schleife

Beitrag von wp_xyz »

Das Problem bei der Programmierung mit RAD-tools wie Lazarus ist, dass vieles auf magische Weise automatisch ausgeführt wird, ohne dass der User Code dafür schreiben muss. Dadurch verliert man die Übersicht, was hinter den Kulissen wirklich passiert, und wird verführt, Annahmen zu machen, die nicht zutreffen.

Egal was du im letzten Post geschrieben hast (das verstehe ich eh nicht), beziehe ich mich auf den im 1.Post gezeigten Code:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
begin
if dclk.value = false then label1.Caption := 'false' else label1.Caption := 'true';
timer1.enabled := true;
   While Dclk.Value = false do
    begin
      if Flag = 1 then break;
      label2.caption := 'Running';
    end;
end;

Das ist offenbar ein Event-Handler eines OnClick-Ereignisses eines Buttons. Also: wenn dieser Button geklickt wird, dann wird dieser Code ausgeführt: Von dem Ändern der Label-Caption abgesehen, wird zunächst der Timer gestartet. Dann kommt die Schleife, falls Dclk.Value false ist, wird Flag auf den Wert 1 geprüft, falls ja, wird die Schleife verlassen; Flag wird offenbar im OnTimer-Event des Timers gesetzt. Die Captions des Labels2 wird verändert - das ist keine einfache Pascal Variablen-Zuweisung, sondern erzeugt auch noch ein Label2.Invalidate, was die Aufforderung, das Label neu zu zeichnen in die Nachrichten-Warteschlange des Betriebssystems einreiht. Mehr passiert hier nicht. Wie gelangt dein Programm nun bitte in den Event-Handler des Timers, in dem der Wert von Flag geändert wird, so dass die Schleife in Button1Click beendet werden kann? Gar nicht! Du musst den Programm die Möglichkeit geben, die Nachrichtenwarteschlange abzuarbeiten - hier würde stehen, dass das Label2 (und auch Label1) neu zu zeichnen sind, und dass ggfs der Timer abgelaufen ist und dessen Routine OnTimer anzuspringen ist. Wie oben schon erwähnt, ist der Befehl dafür eine Methode der Application: Application.ProcessMessages. Also:

Code: Alles auswählen

procedure TForm1.Button1Click(Sender: TObject);
begin
  // if dclk.value = false then label1.Caption := 'false' else label1.Caption := 'true'; 
  // BITTE: if "value = false" is Blödsinn... Besser: "if not value"
  // If benötigt eine boolsche Variable, und dclk.value ist das bereits
  if not dclk.value then label1.Caption := 'false' else label1.Caption := true;
  timer1.enabled := true;
  While Dclk.Value = false do
    begin
      if Flag = 1 then break;
      label2.caption := 'Running';
      Application.ProcessMessages// <-- neu
    end;
end;

Wie auch schon erwähnt wurde, ist das nicht der beste Code, weil der immer-währende Aufruf von Application.ProcessMessages Prozessorlast bewirkt. Besser wäre es, das Warten in einen eigenen Thread zu verlagern.

ppahl
Beiträge: 56
Registriert: Fr 25. Nov 2016, 00:02

Re: Komische Probleme mit (Endlos)Schleife

Beitrag von ppahl »

Vorab: Sorry wenn ich manche Termini vielleicht nicht ganz korrekt verwende, bin halt kein Informatiker sondern nur Hobbyanwender ;).
Ich bin offensichtlich der Fehlannahme unterlegen dass die Timer-Komponente gewissermassen ein eigener Thread ist welcher unabhängig von dem eigentlichen Programm läuft. Und dass dessen OnTimer-Event auch unabhängig davon angesprungen wird wenn die eingestellt Zeit erreicht ist.
Wobei ich auch die Notwendigkeit des Application.ProcessMessages noch nicht so ganz blicke: Ab wann ist die notwendig und wann nicht? Ich habe schon diverse Programme mit durchaus umfangreicheren Prozeduren geschrieben, mir ist aber bislang nicht aufgefallen dass z.B. Label.caption Zuweisungen innerhalb dieser Prozeduren irgendwie verzögert oder sogar gar nicht abgearbeitet worden wären. Wobei ich bislang Delphi 7 benutzt habe, auf Lazarus bin ich nur umgestiegen weil für den RPi verfügbar...sollte aber doch keinen Unterschied machen?

Frank Ranis
Beiträge: 201
Registriert: Do 24. Jan 2013, 21:22

Re: Komische Probleme mit (Endlos)Schleife

Beitrag von Frank Ranis »

Hallo ppahl,

habe aus deinen Info's mal was gebastelt , siehe Anhang .
Da ich keinen Hardware-Input habe , habe ich auf der Form eine Checkbox zur Input-Simulation benutzt , die Du dann mit der Maus ein/ausschalten mußt.

Ohne ein 'application.ProcessMessages' in der While-Schleife geht der Timer nicht , einzig der Code innerhalb wird abgearbeitet.
Wieder was gelernt .

Hoffe Du kannst damit was anfangen .

Gruß

Frank

PS: 'ppahl' ist für mich kein Name , zumindest ein Vorname wäre wünschenswert !
Dateianhänge
Laz Endlosschleife.zip
(837.73 KiB) 68-mal heruntergeladen
www.flz-vortex.de

ppahl
Beiträge: 56
Registriert: Fr 25. Nov 2016, 00:02

Re: Komische Probleme mit (Endlos)Schleife

Beitrag von ppahl »

Wieder was gelernt

Zumindest beruhigend dass das nicht nur mir nicht ad hoc geläufig war ;).
Für mich war es irgendwie selbstverständlich dass ein Timer immer läuft und nicht nur, wenn gerade nix anderes zu tun ist, so kommt er mir etwas sinnentleert vor. Bin ich bislang auch nie drüber gestolpert.

Deinen Code schaue ich mir später mal an, besten Dank jedenfalls schon mal!

Gruss
Anton

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Komische Probleme mit (Endlos)Schleife

Beitrag von wp_xyz »

ppahl hat geschrieben:Für mich war es irgendwie selbstverständlich dass ein Timer immer läuft und nicht nur, wenn gerade nix anderes zu tun ist, so kommt er mir etwas sinnentleert vor.

Wenn ich mich jetzt nicht total täusche, kommt der Timer aus dem Betriebssystem. Er läuft also immer, auch wenn das Programm in einer Totschleife hängt. Nur kann das Programm in diesem Fall nicht auf das OnTimer-Event reagieren, weil es nicht in die Message-Loop zurückkehren und die anstehenden Nachrichten abfragen kann.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Komische Probleme mit (Endlos)Schleife

Beitrag von mschnell »

wp_xyz hat geschrieben:
ppahl hat geschrieben:Für mich war es irgendwie selbstverständlich dass ein Timer immer läuft und nicht nur, wenn gerade nix anderes zu tun ist, so kommt er mir etwas sinnentleert vor.

Wenn ich mich jetzt nicht total täusche, kommt der Timer aus dem Betriebssystem. Er läuft also immer, auch wenn das Programm in einer Totschleife hängt. Nur kann das Programm in diesem Fall nicht auf das OnTimer-Event reagieren, weil es nicht in die Message-Loop zurückkehren und die anstehenden Nachrichten abfragen kann.

Genau !
Wenn ein Programm auch reagieren soll, wenn es anderweitig beschäftigt ist, muss man mit Threads arbeiten. Da das aber diverse zusätzliche Probleme mit sich bringt, sollte man sich das gut überlegen.
Außerdem sind "endlos-Schleifen" ("Busy Wait") auch da verboten, weil das eine 100% Auslastung des ganzen Rechners bewirk6t und damit alles (andere Threads und andere Programme) verlangsamt.

-Michael

Thandor
Beiträge: 153
Registriert: Sa 30. Jan 2010, 18:17
OS, Lazarus, FPC: Windows 10 64Bit/ lazarus 3.0 mit FPC 3.2.2 (32Bit + 64bit)
CPU-Target: 64Bit
Wohnort: Berlin

Re: Komische Probleme mit (Endlos)Schleife

Beitrag von Thandor »

mschnell hat geschrieben:[...]
Außerdem sind "endlos-Schleifen" ("Busy Wait") auch da verboten, weil das eine 100% Auslastung des ganzen Rechners bewirk6t und damit alles (andere Threads und andere Programme) verlangsamt.

-Michael


ein Delay(10); bewirkt hier schon wunder.

ppahl
Beiträge: 56
Registriert: Fr 25. Nov 2016, 00:02

Re: Komische Probleme mit (Endlos)Schleife

Beitrag von ppahl »

Haltet ihr euch nicht ein wenig zu sehr an dem Begriff ENDLOS-Schleife fest? Das Programm kann ja letztlich nicht wissen ob es eine Endlos- oder eine Irgendwann-Beendet-Schleife ist, ergo scheint mir dass generell in so einer Schleife ohne application.ProcessMessages keine andere Aktion wie z.B. Refresh der Labels erfolgt. Oder liege ich da falsch?

creed steiger
Beiträge: 957
Registriert: Mo 11. Sep 2006, 22:56

Re: Komische Probleme mit (Endlos)Schleife

Beitrag von creed steiger »

was du versucht hat ist eine Ereignis-Schleife "nachzubauen".
Das funktioniert so wie du es probiert hast einfach nicht.

Wenn du im Hauptprogramm dich einfach nur um eine Aufgabe "im Kreis drehst", passiert in diesem
Moment halt nichts anderes.

pack deine Abfrage einfach in ein ontimer Event und gut ist.
wie bereits geschrieben wird das periodisch aufgerufen und eine Schleife ist einfach überflüssig.

ppahl
Beiträge: 56
Registriert: Fr 25. Nov 2016, 00:02

Re: Komische Probleme mit (Endlos)Schleife

Beitrag von ppahl »

Was genau soll ich bitte in das OnTimer-Event packen? Das Warten auf den Eingang? Ich fürchte das wird nix.
Offensichtlich hast du nicht verstanden worum es geht: Es soll auf einen Eingang gewartet werden, dieses Warten aber nach einer Zeit X abgebrochen werden. Eine Art 'Interrupt-Light'. Und nicht alle X Sekunden nach dem Eingang geschaut werden, das würde selbst ich hinbringen ;).

Wenn du im Hauptprogramm dich einfach nur um eine Aufgabe "im Kreis drehst", passiert in diesem Moment halt nichts anderes.

'Nichts anderes' ist gut - es geht ja darum dass auch die Anweisung _innerhalb_ des Kreises nicht ausgeführt wird: label2.caption := 'Running' funktioniert nur wenn man es mit application.ProcessMessages erzwingt, ansonsten passiert nix. Woran übrigens auch ein Delay-irgendwas innerhalb der Schleife nichts ändert.
Franks Beitrag entnehme ich dass mir das nicht als einzigem nicht bewusst war.

creed steiger
Beiträge: 957
Registriert: Mo 11. Sep 2006, 22:56

Re: Komische Probleme mit (Endlos)Schleife

Beitrag von creed steiger »

ppahl hat geschrieben: Es soll auf einen Eingang gewartet werden, dieses Warten aber nach einer Zeit X abgebrochen werden. Eine Art 'Interrupt-Light'



es hält dich nichts davon ab den Timer nach der Zeit x zu stoppen

oder wie Eingangs vorgeschlagen Threads zu benutzen
im Examples Verzeichnis sind einige gute Beispiele

Antworten