www.mikrocontroller.net

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


Autor: Frau Holle (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: qwertz (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: qwertz (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Thomas R. (tinman) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: reimann (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: qwertz (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Maik M. (myco)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Frau Holle (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: qwertz (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ;)

Autor: Frau Holle (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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???

Autor: Maik M. (myco)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Frau Holle (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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;

Autor: Maik M. (myco)
Datum:

Bewertung
0 lesenswert
nicht 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 :)

Autor: Frau Holle (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Maik M. (myco)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Frau Holle (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: Zwölf Mal Acht (hacky)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Frau Holle (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Frau Holle (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: rene (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Frau Holle (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: StinkyWinky (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das hatte mir bei meinem Problem mit den Threads unter Delphi geholfen:
http://www.eonclash.com/Tutorials/Multithreading/M...

Autor: Frau Holle (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke, das ziehe ich mir auch rein!

Autor: Markus Müller (mmvisual)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Reinhard Kern (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Frau Holle (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Tany (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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;

Autor: Frau Holle (Gast)
Datum:

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

Autor: Maik M. (myco)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Tany (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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;

Autor: Maik M. (myco)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Markus Müller (mmvisual)
Datum:

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

Autor: Frau Holle (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.