Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Threads von außen beeinflussen [gelöst]
Klabautermann - Di 15.07.03 17:32
Titel: Threads von außen beeinflussen [gelöst]
Hallo,
ich bin momentan dabei mein wissen über Treads zu erweitern.
Die Frage heute, darf ich Thread-Objekte public funktionen verpassen und die dann aufrufen, und laufen diese dann auch in dem Thread zu dem sie gehören oder laufen die im Aufrufenden Thread?
Ein Beispiel. Ich habe ein Projekt mit zwei Units, beide kennen sich. Unit1 Enhällt ein Formular mit 1 Label und 1 knopf. Unit2 enthällt ein Thread Objekt. Der Thread hat eine nicht öffendliche Integer Variable
i welche mit 0 (null) initialisiert wird. Deswelteren gibt es eine eine public procedure
Inc, welche den Wert
i um 1 erhöht und das ergebnis auf den Label ausgibt.
Inc wird immer dann aufgerufen wenn im Formular von Unit1 auf den Knopf gedrückt wird.
Im Quelltext sieht das so aus:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Unit2, StdCtrls;
type TForm1 = class(TForm) Label1: TLabel; Button1: TButton; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); private Thread : tTestThread; public end;
var Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject); begin Thread := tTestThread.create(FALSE); end;
procedure TForm1.Button1Click(Sender: TObject); begin Thread.Inc; end;
end. |
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57:
| unit Unit2;
interface
uses Classes;
type tTestThread = class(TThread) private i : Integer; protected Procedure ShowValue; procedure Execute; override; public procedure Inc; constructor create(CreateSuspended: Boolean); end;
implementation
USES Unit1, Windows, SysUtils;
constructor tTestThread.create(CreateSuspended: Boolean); begin inherited; i := 0; end;
procedure tTestThread.Execute; VAR MSG : TMsg; begin WHILE (NOT Terminated) DO BEGIN IF (PeekMessage(Msg, 0, 0, 0, PM_Remove)) THEN BEGIN TranslateMessage(Msg); DispatchMessage(Msg) END ELSE Sleep(0); END; end;
procedure tTestThread.Inc; begin i := i + 1; Synchronize(ShowValue); end;
procedure tTestThread.ShowValue; begin Form1.Label1.Caption := IntToStr(i); end;
end. |
Mich interessiert nun ob man das so machen darf, oder ob es zu problemen kommt? Muss man noch etwas beachten, oder läuft das anders als ich es mir vorstrelle? Gibt es einen anderen (besseren) Weg um einen Thread, der ansonsten unabhängig läuft Befehle zu geben?
Fragen über Fragen, währe nett wenn mir der eine oder die andere ein paar Antworten geben könnte.
Gruß
Klabautermann
tommie-lie - Di 15.07.03 19:10
AFAIK wird nur der Inhalt von Execute in den Thread abgespalten, alles andere bleibt im aufrufenden Thread (rufst du aus Execute heraus eine Methode des TThread-Abkömmlings auf, ist diese also ebenfalls im Thread!!!).
Da du in Execute, also in dem zweiten Thread, nicht auf
i zugreifst, wäre der Code so also in Ordnung. Das Synchronize(ShowValue) ist also gar nicht nötig, da das Label im Speicherbereich des gleichen Threads liegt. Würdest du beispielsweise in Execute im Sekundentakt
i erhöhen und das auf das label ausgeben, bräuchtest du Synchronize, weil du aus dem Parallelthread (Execute) versuchst auf das Label aus dem Hauptthread zuzugreifen.
Eine Möglichkeit, einem Thread irgendetwas mitzuteilen, wäre PostThreadMessage. Da du ja schon eine Nachrichtenschleife hast (in Execute), wäre das die Stnadardlösung. Da du aber (noch?) kein Fenster hast, bringt dir DispatchMessage nichts, stattdessen so:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| procedure tTestThread.Execute; VAR MSG : TMsg; begin WHILE (NOT Terminated) DO BEGIN IF (PeekMessage(Msg, 0, 0, 0, PM_Remove)) THEN BEGIN TranslateMessage(Msg);
case Msg.message of MyMsg_AbortThread: Terminated := True; end;
END ELSE Sleep(0); END; end; |
Du musst also das, was normalerweise in der WndProc steht, direkt in deiner Schleife machen.
MyMsg_AbortThread sollte eine Konstante sein und auf jeden Fall größer als WM_USER sein, beuispielsweise
Delphi-Quelltext
1: 2: 3: 4:
| const MyMsg_AbortThread = WM_USER + 1; AnotherMessage = WM_USER + 2; |
Wieder eine andere Möglichkeit ( :-) ) wäre es, Variablen zu setzen (von außen) und in einer Endlosschleife innerhalb von Execute per Polling abzufragen. Das dürfte wohl die uneffektivste Methode sein, ein Beispiel bedarf es hier wohl nicht ;-)
Klabautermann - Di 15.07.03 21:15
| tommie-lie hat folgendes geschrieben: |
| Wieder eine andere Möglichkeit ( :-) ) wäre es, Variablen zu setzen (von außen) und in einer Endlosschleife innerhalb von Execute per Polling abzufragen. Das dürfte wohl die uneffektivste Methode sein, ein Beispiel bedarf es hier wohl nicht ;-) |
Dadran hatte ich auch schon gedacht, aber das war mir zu "hässlich".
| tommie-lie hat folgendes geschrieben: |
| AFAIK wird nur der Inhalt von Execute in den Thread abgespalten, alles andere bleibt im aufrufenden Thread (rufst du aus Execute heraus eine Methode des TThread-Abkömmlings auf, ist diese also ebenfalls im Thread!!!). |
Mir war als hätte ich auch mal soetwas gelesen, deshalb fragte ich so misstrauisch.
| tommie-lie hat folgendes geschrieben: |
| Eine Möglichkeit, einem Thread irgendetwas mitzuteilen, wäre PostThreadMessage. Da du ja schon eine Nachrichtenschleife hast (in Execute), wäre das die Stnadardlösung. Da du aber (noch?) kein Fenster hast, bringt dir DispatchMessage nichts |
Ich hoffe doch das darf ich Kombinieren ;).
Ich habe mal mein Beispiel so umgearbeitet:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
| unit Unit2; [...] const MyMsg_AbortThread = WM_USER + 1; MyMsg_Inc = WM_USER + 2; [...] procedure tTestThread.Execute; VAR MSG : TMsg; begin WHILE (NOT Terminated) DO BEGIN IF (PeekMessage(Msg, 0, 0, 0, PM_Remove)) THEN BEGIN TranslateMessage(Msg); case Msg.message of MyMsg_AbortThread: Self.Terminate; MyMsg_Inc: Self.Inc; ELSE DispatchMessage(Msg); end; END ELSE Sleep(0); END; end; |
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| unit Unit1; [...] procedure TForm1.Button1Click(Sender: TObject); begin PostThreadMessage(Thread.ThreadID, MyMsg_Inc, 0, 0); end; |
Das funktioniert auch ganz wunderbar. Wenn ich dich richtig verstanden habe, dann jetzt auch in der Form wie ich es mir wünsche, nähmlich so, dass die wahnsinnig komplizierte Berechnung im Thread2 gemacht wird und erst wenn sie Fertig ist, die Ausgabe in das Label des Haupt Threads gemacht wird.
Wenn das so stimmt dann danke ich dir viel mals, und werde mcih jetzt mit den zufriedenen Gefühl wieder etwas gelernt zu haben über mein Abendessen her machen ;).
Gruß
Klabautermann
tommie-lie - Di 15.07.03 21:59
| Klabautermann hat folgendes geschrieben: |
| tommie-lie hat folgendes geschrieben: | | Polling |
Dadran hatte ich auch schon gedacht, aber das war mir zu "hässlich". |
Stimmt, deswegen sprach ich auch von uneffektivst ;-)
| Zitat: |
| Mir war als hätte ich auch mal soetwas gelesen, deshalb fragte ich so misstrauisch. |
Also ich bin mir zu 95% sicher.
| Zitat: |
| Wenn ich dich richtig verstanden habe, dann jetzt auch in der Form wie ich es mir wünsche, nähmlich so, dass die wahnsinnig komplizierte Berechnung im Thread2 gemacht wird und erst wenn sie Fertig ist, die Ausgabe in das Label des Haupt Threads gemacht wird. |
Ja, die wahnsinnig komplizierte Berechung findet nun im zweiten Thread statt. Das kannst du auch ausprobieren, indem du die Rechnung noch viel wahnsinnig schwerer machst (z.B. durch sleep *g*) und im Hauptthread und im zweiten Thread ein paar ShowMessages an diverse Stellen einfügst.
Allerdings wird jetzt das Synchronize zwingend notwendig!
| Zitat: |
| Wenn das so stimmt dann danke ich dir viel mals, und werde mcih jetzt mit den zufriedenen Gefühl wieder etwas gelernt zu haben über mein Abendessen her machen ;). |
Aber keine Krümel auf die Tastatur! :mrgreen:
Klabautermann - Di 15.07.03 22:44
Hi,
| tommie-lie hat folgendes geschrieben: |
| Ja, die wahnsinnig komplizierte Berechung findet nun im zweiten Thread statt. |
hervorragend, dann kann ich die Tage ja mal was realisieren, was mehr sinn macht als der Beispiel Thread :mrgreen:.
| tommie-lie hat folgendes geschrieben: |
| Allerdings wird jetzt das Synchronize zwingend notwendig! |
Damit und mit der Tatsache, dass ich mir ein paar Messages definieren muss kann ich problemlos leben.
| tommie-lie hat folgendes geschrieben: |
| Aber keine Krümel auf die Tastatur! :mrgreen: |
Nicht doch, ich bin dafür extra in einen anderen Raum gegangen (sonst hätte ich zum Essen ja auch keine Hände frei :roll:).
Gruß
Klabautermann
tommie-lie - Di 15.07.03 23:35
Titel: r nciht geschrieben.
| Klabautermann hat folgendes geschrieben: |
| tommie-lie hat folgendes geschrieben: | | Allerdings wird jetzt das Synchronize zwingend notwendig! |
Damit und mit der Tatsache, dass ich mir ein paar Messages definieren muss kann ich problemlos leben. |
Du kannst auch selbst eine CriticalSection benutzen. Dafür gibt's auch ein VCL-Objekt.
Aber Synchronize wird das schon selber machen, wäre nur ein Hinweis für Wissbegierige :-P
Klabautermann - Mi 16.07.03 00:03
Titel: Re: r nciht geschrieben.
Hi,
| tommie-lie hat folgendes geschrieben: |
Du kannst auch selbst eine CriticalSection benutzen. Dafür gibt's auch ein VCL-Objekt.
Aber Synchronize wird das schon selber machen, wäre nur ein Hinweis für Wissbegierige :-P |
noch so 'ne Sache die ich momentan nicht zuordnen kann

, ich habe mal im Zug was drüber gelesen und meine das es auch nicht so schwierig war, da fehlte aber die Praxis zum verfesstigen.
Also kommt Praxis kommt wissen.
Auf jeden Fall hast du mich jetzt soweit auf den Damm gebracht, dass ich meine nächsten Ideen verwirklichen kann. Wenn Probleme auftauchen oder ich auf neue Ideen komme weiß ich ja wo ich hilfe finde ;).
Gruß
Klabautermann
tommie-lie - Mi 16.07.03 14:11
Titel: Re: r nciht geschrieben.
| Klabautermann hat folgendes geschrieben: |
| tommie-lie hat folgendes geschrieben: | | CriticalSection |
noch so 'ne Sache die ich momentan nicht zuordnen kann , ich habe mal im Zug was drüber gelesen und meine das es auch nicht so schwierig war, da fehlte aber die Praxis zum verfesstigen. |
Stimmt, schwierig ist es nicht. Die Klasse heißt glaub' ich TCriticalSection. Beim start des programmes sollte man sie createn, beim beenden wieder freigeben. Wie dann die Befehle zum eintreten und verlassen genau heißen, weiß ich nciht mehr, sieht man aber in der CodeCompletion ;-)
Auf jeden Fall musst du in eine CS "eintreten", dann werden alle anderen Threads des gleichen Prozesses auf Eis gelegt und du kannst beliebig Variablen schreiben, ohne daß Gefahr besteht, daß ein anderer Thread gleichzeitig drauf zugreift. Beim verlassen der CS werden die anderen Threads dann wieder gestartet.
Für Zahlen und Pointer bieten sich übrigens die API-Funktionen InterLockedXXX an, einfach mal im SDK schauen. Die tauschen Variablen aus und schützen den Speicher autoamtisch, praktisch wenn man überlegt, ob man wegen einem einzigen Integer extra 'ne CS benutzen muss ;-)
| Zitat: |
| Wenn Probleme auftauchen oder ich auf neue Ideen komme weiß ich ja wo ich hilfe finde ;). |
Jupp :-)
Brueggendiek - Mi 16.07.03 21:53
Hallo!
| tommie-lie hat folgendes geschrieben: |
| Auf jeden Fall musst du in eine CS "eintreten", dann werden alle anderen Threads des gleichen Prozesses auf Eis gelegt und du kannst beliebig Variablen schreiben, ohne daß Gefahr besteht, daß ein anderer Thread gleichzeitig drauf zugreift. Beim verlassen der CS werden die anderen Threads dann wieder gestartet. |
Das ist so nicht richtig!
Bei einer Critical Section kann nur einer eintreten. Versucht ein anderer Thread, auch einzutreten, muß er warten, bis frei ist. Wer nicht eintreten will, kann weitermachen.
Es muß also in allen Fällen, wo gemeinsame Daten geändert werden, am Anfang "cs.Enter" und am Ende "cs.Leave" stehen. Daß wir das Ganze mit einer kleinen Prise "TRY- FINALLY" garnieren, sollte selbstverständlich sein.
Gruß
Dietmar Brüggendiek
tommie-lie - Mi 16.07.03 23:33
| Brueggendiek hat folgendes geschrieben: |
Das ist so nicht richtig!
Bei einer Critical Section kann nur einer eintreten. Versucht ein anderer Thread, auch einzutreten, muß er warten, bis frei ist. Wer nicht eintreten will, kann weitermachen.
Es muß also in allen Fällen, wo gemeinsame Daten geändert werden, am Anfang "cs.Enter" und am Ende "cs.Leave" stehen. Daß wir das Ganze mit einer kleinen Prise "TRY- FINALLY" garnieren, sollte selbstverständlich sein. |
Hmm, wo zum ...
ahhh ja... hier:
| Thread-Tutorial von Delphi-Source.de hat folgendes geschrieben: |
| Da alle anderen Threads blockiert werden, während sich ein Thread im kritischen Abschnitt befindet, wird die Anwendung langsamer. Kritische Abschnitte sollten also nur verwendet werden, wenn es erforderlich ist. |
Was du gesagt hast, würde bedeuten, daß alle anderen Threads des gleichen Prozesses weiterlaufen, bis sie ebenfalls in eine CS eintreten wollen, dann warten sie, bis der andere Threead draußen ist.
Das beißt sich aber irgendwie mit der Aussage aus dem Tutorial.
Nach dem, was ich eben im SDK nachgelesen habe, scheinst du aber Recht zu haben. Die (API-)Funktion EnterCriticalSection wartet selbstständig, bis die CS vom aufrufenden Thread betreten werden darf, also bis der Thread, der im Moment drin ist, sie wieder verlassen hat. Das schließt natürlich aus, daß alle anderen Threads automatisch gestoppt werden, denn dann müsste EnterCriticalSection ja nicht mehr warten, weil es ja nix zu warten gibt.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!