Forum: PC-Programmierung Delphi - wie verschiedene Prozessabläufe von anderen abhängig machen


von Frau Holle (Gast)


Lesenswert?

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?

von qwertz (Gast)


Lesenswert?

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.

von qwertz (Gast)


Lesenswert?

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.

von Thomas R. (tinman) Benutzerseite


Lesenswert?

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.

von reimann (Gast)


Lesenswert?

> 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.

von qwertz (Gast)


Lesenswert?

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.

von Maik M. (myco)


Lesenswert?

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.

von Frau Holle (Gast)


Lesenswert?

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.

von qwertz (Gast)


Lesenswert?

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 ;)

von Frau Holle (Gast)


Lesenswert?

>>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???

von Maik M. (myco)


Lesenswert?

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.

von Frau Holle (Gast)


Lesenswert?

>>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;

von Maik M. (myco)


Lesenswert?

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 :)

von Frau Holle (Gast)


Lesenswert?

>>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.

von Maik M. (myco)


Lesenswert?

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.

von Frau Holle (Gast)


Lesenswert?

>>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).

von Purzel H. (hacky)


Lesenswert?

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.

von Frau Holle (Gast)


Lesenswert?

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...

von Frau Holle (Gast)


Lesenswert?

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.

von rene (Gast)


Lesenswert?


von Frau Holle (Gast)


Lesenswert?

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!

von StinkyWinky (Gast)


Lesenswert?

Das hatte mir bei meinem Problem mit den Threads unter Delphi geholfen:
http://www.eonclash.com/Tutorials/Multithreading/MartinHarvey1.1/ToC.html

von Frau Holle (Gast)


Lesenswert?

Danke, das ziehe ich mir auch rein!

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

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.

von Reinhard Kern (Gast)


Lesenswert?

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

von Frau Holle (Gast)


Lesenswert?

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.

von Tany (Gast)


Lesenswert?

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;

von Frau Holle (Gast)


Lesenswert?

cool, den Sleep-Befehl habe ich noch nicht gekannt.
Das sollte dann so eigentlich funktionieren. Tausend Dank für diese 
supersimple Lösung!

von Maik M. (myco)


Lesenswert?

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.

von Tany (Gast)


Lesenswert?

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;

von Maik M. (myco)


Lesenswert?

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.

von Markus M. (Firma: EleLa - www.elela.de) (mmvisual)


Lesenswert?

Wieso machst Du es nicht so wie ich oben beschrieben habe?

von Frau Holle (Gast)


Lesenswert?

@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
Noch kein Account? Hier anmelden.