Hallo,
ich bin wohl zu doof für Delphi oder objektorientierte Programmierung.
Aber vielleicht kann hier jemand Licht ins Dunkle für mich bringen.
In Unit1 (in Form1) habe ich eine Schleife die ich 200 mal durchlaufe
und jedesmal einen Prozess in Unit2 aufrufe
Prinzip der Hauotschliefe in Unit 1
for Schleife1:=1 to 200 do begin
anweisung1;
Form2.testbuttonClick(Sender); // start Prozess2 in Unit2 (Form2)
anweisung3;
end;
So nun mein Problem:
1. Ich weiß nicht wie lange der Prozess2 in Unit2 dauert.
2. Die Schleife darf aber erst fortgeführt werden, wenn der Prozess in
Unit2 fertig ist.
Meine Kleinhirn-Idee war, ein Flag zu Beginn des Prozesses2 von Unit2 zu
setzen und dieses Flag in diesem Pozess2 erst wieder zu löschen, wenn
dieser Prozess2 fertig ist, z.B. mit anweisung3:
while flag=TRUE do begin
end;
Das würde aber nur funktionieren, wenn der Prozess2 in dieser Zeit
ungehindert ablaufen könnte. Aber offenbar beisst er sich in dieser
while-schleife fest und der Prozess2 wird nicht weiter bearbeitet.
In Basic konnte ich das früher das mit einem GoSub-Befehl realisieren.
Die Sub wurde erst komplett bearbeitet und danach von der Sub in das
Hauptprogramm zurückgesprungen. Also idiotensicher.
In Delphi komme ich da nicht weiter (außer ich würde das umständlich mit
vielen Timer-Komponenten lösen).
Ich habe da wohl ein prinzipielles Verständnisproblem.
Kann mir jemand die Scheuklappen von den Augen nehmen?
dein Form2-Aufruf startet keinen anderen "Prozess", sondern sendet eine
Nachricht an die Warteschlange von Form2, dass bei Gelegenheit ein
Click-Ereignis verarbeitet werden soll. Die Warteschlange kann jedoch
nicht geleert werden, da der Prozess ja mit deiner Endlosschleife
beschäftigt ist...
Echte parallele Prozesse gehen auch, gibts eine Unit für, leider ist das
zu lange her als das ich noch Details wüsste, Beispiele gibts aber genug
im Internet (stichwort Threads)
Sonst:
du könntest die Funktion aus Form2 starten und dann die Funktion aus
Form1 beenden, so dass die Funktion aus Form2 abgearbeitet wird und
diese dann eine Callback-Funktion aufruft, die wiederum den nächsten
Aufruf der Funktion aus Form2 erzeugt und sich beendet ...
Außerdem gibt es einen Befehl der die Laufzeitumgebung auffordert erst
einmal alle Nachrichten aus der Warteschlange zu bearbeiten.
Application.update eventuell? Alles sehr lange her, auf jeden Fall
irgendwas mit Application. Schau mal in die Hilfe oder ins Netz.
Oh, oder du rufst die Funktion direkt auf und nicht den Handler (was du
scheinbar machst, sonst würde er schon so auf die andere Funktion
warten), dann sollte die erste Funktion warten, egal aus welchem Form
sie kommt.
jede zeile ist wie ein gosub, es muss aber schon sinn ergeben.
Form2.testbuttonClick(self); wird ein "klick" verursachen, "sender" ist
schlecht.
Du kannst auch mit if abfragen den zustand abfragen, z.b. welche mouse
taste geklickt hat.
> offenbar beisst er sich in dieser while-schleife fest
Du musst innerhalb dieser Schleife dem Windows-Kernel die Möglichkeit
geben, "andere" Prozesse zu bedienen. Ich habe vor längerer Zeit mit
Visual Basic gearbeitet. Dort gab es eine Anweisung, die in die Schleife
einzubauen war, um eben dieses zu gewährleisten. Leider fällt mir der
korrekte Name momentan nicht mehr ein, aber vielleicht kann jemand
anderes dir noch besser helfen.
Sender kann schon auch gehen, falls der definiert ist (Standardmäßig ja
in Ereignishandlern das aufrufende Objekt, könnte man z.B. verwenden um
zwischen mehreren Forms die die gleiche Funktion aufrufen zu
unterscheiden) Sollte eigentlich egal sein in seinem Fall.
ehm, irgendwie sind die bisherigen Antworten etwas Delphi-fremd.
In Delphi laufen alle Formulare im selben Windows Thread, daher wenn man
von einem Formular eine Funktion eines anderen Formulars aufruft, wird
das sequentiell verarbeitet, da es Funktionen des selben Threads sind.
Die Message-Verarbeitung hat rein garnichts damit zu tun, denn mit dem
Aufruf "Form2.testbuttonClick(Sender);" wird das Message-System komplett
umgangen und es wird direkt eine Funktion aufgerufen.
Sender ist in dem Fall der Parameter der an die Funktion übergeben wird,
in der die Schleife läuft, und für testbuttonClick ist es eigentlich
völlig wurscht, wer der Sender ist, solange man in der Funktion nicht
selbst irgendetwas mit dem Parameter "Sender" anfängt.
Die direkteste Lösung wäre wohl, die Schleife in einen eigenen Thread zu
stecken (eine abgeleitete Klasse von TThread), und diesen Thread dann in
Form1 zu starten. In dem Thread muss dann allerdings darauf geachtet
werden, das die Funktion von Form2 mit der Funkion "synchronize"
aufgerufen werden muss.
erst mal vielen Dank für die schnellen Stichworte.
Die helfen mir schon mal weiter, nach der richtigen Vorgehensweise zu
suchen.
die Begriffe Handle, Application und vor allem Synchronize usw. machen
mich sicherlich schlauer...
Falls jemand noch einen guten Hilfeansatz hat, freue ich mich natürlich.
Maik M. schrieb:> Die Message-Verarbeitung hat rein garnichts damit zu tun, denn mit dem> Aufruf "Form2.testbuttonClick(Sender);" wird das Message-System komplett> umgangen und es wird direkt eine Funktion aufgerufen.
Dachte ich auch, aber er behauptet ja die Funktion von Form2 wird nicht
ausgeführt? Oder hab ich da was falsch gelesen? So kam ich auf das
Messageproblem...
Und ja, meine aktive Delphi-Zeit liegt einige Jahre zurück ;)
>>Dachte ich auch, aber er behauptet ja die Funktion von Form2 wird nicht
ausgeführt?
Ja, das ist so. Das erste, was ich in Form 2 dann mache (diese ist die
ganze Zeit über sichtbar) ist, einen RadioButton zu setzen.
Dies geschieht aber nicht, wenn ich die "while Schleife" in Form 1 nach
dem Aufruf drin habe. Lasse ich die "while-Schleife" testhalber weg,
dann wird der RadioButton auch wunderbar gesetzt.
Also offenbar keine Parallelität???
qwertz schrieb:> Dachte ich auch, aber er behauptet ja die Funktion von Form2 wird nicht> ausgeführt?
Jetzt wo du's erwähnst, stimmt... aber ich denke er hat da eine While
schleife drin, die eine Variable aus Prozess 1 überprüft, die niemals
geändert wird, weil Prozess 1 nicht weiter ausgeführt wird.
>>aber ich denke er hat da eine While>>schleife drin, die eine Variable aus Prozess 1 überprüft, die niemals>>geändert wird, weil Prozess 1 nicht weiter ausgeführt wird.
nein, ich frage tatsächlich nur das Flag aus Form2, Prozess2 ab.
Mehr steht in der Schleife nicht drin:
while (Form2.RBaktiv.Checked=TRUE) do begin
end;
dann hängt er solange fest, bis du den RadioButton checkst...
BTW:
while Form2.RBaktiv.Checked do;
die Klammern kann man bei Delphi weglassen, und Boolean braucht man in
kaum einer Programmiersprache mit true vergleichen :)
>>dann hängt er solange fest, bis du den RadioButton checkst...
aber genau das mache ich doch in der while-Schleife??
Solange dieser RadioButton gesetzt ist, hört die Schleife nicht auf.
Und dieser Button würde erst dann rückgesetzt, wenn der Prozess2 zu Ende
ist.
Aber der Prozess2 kommt durch die While-Schleife eben offenbar gar nicht
zum Zug.
Also hier findet keine parallele Abarbeitung der Threads statt.
Ich muss das also komplett anders angehen. Ich weiß nur noch nicht wie.
Irgendwie kappier ich das gerade garnicht, so wie ich das verstanden
habe schaut es dann so aus:
while Form2.RBaktiv.Checked do
begin
anweisung;
if irgendwas then Form2.RBaktiv.Checked := true;
end;
damit die while schleife überhaupt gestartet wird, muss der RadioButton
vor dem Start gecheckt sein.
>>Irgendwie kappier ich das gerade garnicht, so wie ich das verstanden>>habe schaut es dann so aus:>>while Form2.RBaktiv.Checked do>>begin>> anweisung;>> if irgendwas then Form2.RBaktiv.Checked := true;>>end;>>damit die while schleife überhaupt gestartet wird, muss der RadioButton>>vor dem Start gecheckt sein.
Nein sorry, wenn ich mich so unklar ausdrücke.
Das erste was im Prozess2 in Form2 passiert ist das setzen des
RadioButtons "RBaktiv". Das ist mein "Flag".
Die Abfrageschleife besteht wirklich nur aus der nun mehr einen(!) Zeile
(Dein Tip) siehe unten.
for Schleife1:=1 to 200 do begin
anweisung1;
Form2.testbuttonClick(Sender); // start Prozess2 in Unit2 (Form2)
while Form2.RBaktiv.Checked do;
end;
Die Eigenschaft Form2.RBaktiv.checked wird tatsächlich noch in Form2
gesetzt (sonst würde die Schleife nicht hängen; vorher ist er überigens
noch nicht gesetzt! Das kann ich im sichtbaren Formular zur Laufzeit
überpüfen!). Der Thread in Form2 wird also nicht weiter bearbeitet
sondern nur noch die Schleife bedient. Daher kann der Prozess in Form2
das Flag (den Buttonzustand) auch nicht löschen.
Entferne ich die While-Schleife, dann geht alles wunderbar, nur eben
nicht sequentiell sondern viel zu schnell (reimt sich schön).
Der Formclick wird direkt ausgefuehrt. Da ist nichts mit Prozessen.
Prozesse heissen TThread und sind nicht ganz einfach zum Einsteigen.
Was soll's denn werden ?
Mit "while ..active do ;" wird auf keinen Fall etwas. Falls man das Form
updaten will waehrend eine Schleife laeuft, dann waere allenfalls
"Application.Processmessages" etwas.
Danke, dass das mit dem TThread anfangs kryptisch ist, habe ich bereits
bemerkt, da ich bei meiner Recherche auf "TThread.waitfor" gestossen
bin, was wigentlich der Allheilbringer wäre. Aber ich kapier es leider
nicht, da ich vermutlich alles umschmeissen müsste.
Ich habe leider bereits eine fertige Unit, in welcher ich viele
RS232-Operationen ablaufen lasse (das ist meine Unit2 in den obigen
Beiträgen). Ich übergebe die Datenbuffer eben 200x an diese Unit und
sollte in meiner Hauptunit jedesmal warten, bis die Datenübertragung
abgeschlossen ist.
In meiner Unit 2 laufen verschiedene Prozesse ab, die wechselseitig
arbeiten (PingPong RS232-Übertragung byteweise und blockweise und nicht
zeitdeterminisitisch). Wenn die Übertragung fertig ist (erfolgreich oder
mit Fehlermeldung) dann kann ich den nächsten der 200 Datenblöcke
übergeben und die Ablaufprozeduren in Unit2 erneut starten.
Aber ohne "Thread-Waiter" werden eben die 200 Datensätze sofort
nacheinander übertragen, da mein Aufruf der Anfangsprozedur in Unit2
meinen Hauptprozess in Unit1 nicht anhält.
Ich fürchte, ich muss einfach ganz stupide die Sache mit Timern angehen
und immer gehörig "Luft" lassen. Das bremst die Applikation zwar gehörig
aus und ist mehr als unsauber gelöst, aber die TThread.Thematik ist mir
noch etwas zu hoch...
Was war ich doch früher für ein glücklicher Basic-Gosub-Programmierer...
Meine Lösung für Blöde (falls es jemanden interessiert):
Den Inhalt meiner Hauptschleife verlagere ich in ein Timereignis Timer1.
In der Hauptschleife (200x) wird nur dieser Timer1 gestartet. Beim
ersten mal mit sofortiger Auslösung des Timerereignisses.
In der Timerroutine erfolgt der Aufruf der Unit2 und der Timer1 wird
danach erneut gestartet (mit großem Zeitintervall, welches alle
Eventualitäten einschließt).
Sobald die Prozesse in Unit2 fertig sind, wird von dort das
Timerintervall von Timer1 in Unit1 auf den Minimalwert (1ms)
runtergesetzt, so dass das Timerereignis Timer1 sofort ausgelöst wird.
Damit kann der Unit2-Aufruf von Unit1 aus erneut gestartet werden.
Das funktioniert nun wie gewünscht. Falls jemand dennoch eine
idiotensichere Erklärung für eine "ordentliche" Lösung ohne Timerumweg
hat, wäre ich sehr dankbar.
Ansonsten vielen Dank allen, die mir geholfen haben.
Das geht genau in die Richtung, die ich mir vorgestellt habe. Bei
Gelegenheit werde ich mir das mal reinziehen, da Du schön knapp den
Ablauf mit TThreads beschreibst, so dass ich es vermutlich auch kapieren
kann.
Momentan bin ich erst mal froh, dass meine Schmalspur-Version auch
funktioniert.
Aber offenbar ist das Thema einer ordentlichen sequentiellen
Prozess-Ablaufstruktur für absolute beginners in Delphi gar nicht so
trivial und meine Schwierigkeiten damit nicht ganz unbegründet.
Vielen Dank rene!
Ein extra Thread ist nicht nötig. Wenn man damit anfängt und nur eine
Kleinigkeit unsauber proggt, dann bringt es spätestens beim Beenden der
EXE eine unschöne "Zugriffsverletzung".
Mache lieber eine Message-Schleife.
Sobald Unit2 fertig ist (was sie offensichtlich selbst weiß) eine
Funktion aus der Unit1 aufrufen:
Prinzip der Unit 2
Unit1.DoNextSchleife();
Fertig ist die laube. Kein Timer, kein garnichts.
Nach IMPLEMENTATION muss in der Unit2 ein USES mit Unit1 stehen.
PS: In Delphi/Lazarus ist das ganze etwas einfacher als mit C++, daher
besser das nächste mal in einem Delphi-Forum nachfragen.
Hallo,
alles viel zu kompliziert. Das Problem besteht nur darin, dass
"Form2.testbuttonClick(Sender); // start " den gewünschten Ablauf eben
nur startet. Ruft man an dieser Stelle eine Prozedur auf, die den
gewünschten Prozess komplett ablaufen lässt (was ja auch eigentlich das
Normale wäre), arbeitet die Schleife wie gewünscht.
Etwa so
for Schleife1:=1 to 200 do begin
anweisung1;
ExecuteProcess2;
anweisung3;
end;
Gruss Reinhard
Danke wieder mal.
Aber das mit der einzelnen Prozedur funktioniert hier eben leider nicht,
da in Unit2 jede Menge verschiedenen RS232-Ereignisse ablaufen, nach
denen der Ablauf in Unit2 gesteuert wird (viele verschiedene
Prozeduren).
Die Unit2 ist also eher wie ein eigenständiges Programm zu betrachten,
welches durch das KlickEreignis nur gestartet wird.
geht's nicht so?:
procedure Form2.testbuttonClick(Sender:Tobject);
begin
....
Testbutton.Tag:=1;
end;
for Schleife1:=1 to 200 do
begin
Form2.Testbutton.Tag:=0
Form2.TestbuttonClick(self);
while Form2.Testbutton.Tag =0 do Sleep;
...;
...;
end;
Sleep benötigt einen Parameter der die Dauer angibt... solange sleep
läuft, hängt der Thread fest, in diesem Fall wäre es sogar der Thread
der VCL, und damit hängt dann auch die GUI.
ja, sleep braucht sicherlich einen Parameter, sonnst gibt's
Fehlermeldung.
eigentlich kann man den sleep weglassen:
while Form2.Button1.Tag=0 do
begin
// leere Schleife
end;
ohne Sleep wird ein Kern der CPU komplett ausgelastet...
in deinem Beispiel wäre das aber eh egal, da "Form2.testbuttonClick"
immer vor der While-Schleife ausgeführt und damit die While-Schleife
garnicht betreten würde, da Form2.Testbutton.Tag an dieser Stelle immer
1 ist.
Wie gesagt, solange der Hauptthread der Anweisung etwas zu tun hat, kann
man nicht ohne weiteren Thread Sachen abbarbeiten. Mit der Timer-Version
hat Frau Holle das Problem umgangen, aber Timer werden auch nur
sequentiell abgearbeitet, dh. wenn der Hauptthread festhängt wird der
Timer erst ausgeführt wenn der Hauptthread wieder im Leerlauf ist...
also auch nicht optimal.
@Markus Müller
Oh sorry, den Beitrag habe ich irgendwie überlesen oder nicht auf Anhieb
verstanden, dass das eigentlich entwaffnend schlau ist.
Klar, die Schleife von Unit1 kann ich auch von Unit2 aus ansteuern. Das
ist wohl die einfachste Lösung.
Ich bin nur gerade so zufrieden mit meiner Notlösung, dass ich die
Optimierungen (also auch diese unsägiche Geschichte) erst am Ende meines
Projektes in Angriff nehme (komme langsam etwas unter Zeitdruck...).
Nochmal vielen Dank allen, die mir geholfen haben.
Ich lasse es für Euch dafür auch gerade immer mal wieder kräftig
schneien!