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.
Ich hab mal was gemacht mit Uarts : http://www.ibrtses.com/delphi/commthread.html http://www.ibrtses.com/delphi/threads.html
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!
Das hatte mir bei meinem Problem mit den Threads unter Delphi geholfen: http://www.eonclash.com/Tutorials/Multithreading/MartinHarvey1.1/ToC.html
Danke, das ziehe ich mir auch rein!
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;
cool, den Sleep-Befehl habe ich noch nicht gekannt. Das sollte dann so eigentlich funktionieren. Tausend Dank für diese supersimple Lösung!
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.
Wieso machst Du es nicht so wie ich oben beschrieben habe?
@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!
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.