![]() |
Erste Schritte Multi-Threading
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Zusammen,
ich habe eine Client/Server Applikation, in welcher die Server-App einige aufwändige Abfragen auf unterschiedlichen Datenbanken durchführt. Diese Abfragen dauern ein paar Sekunden und wenn es unglücklich läuft, blockiert eine Abfrage eine andere. Das gibt dann eine Exception, die zwar abfgefangen werden, aber ich möchte das Ganze gerne in den Griff bekommen. Daher wage ich mich erstmalig an das Thema Multi-Threading heran. Ich habe eine ganz banale App geschrieben, in der ich die ersten Versuche bestritte habe und ich würde gerne mal ein Feedback von Euch hören, für die das Routine ist.
Delphi-Quellcode:
Dann habe ich auch noch zwei konkrete Frage: Wenn ich einen der beiden Threads durch erneutes Klicken des "Eins_Start" starte, während der Thread noch läuft, dass dann quasi ein weiterer Thread gestartet wird. Erkennbar, dass die Ausgabe zwischen den beiden Threads wechselt. Wie ist das möglich? Was für ein Thread wird denn da erzeugt? Ich habe doch mit einer globalen Variable gearbeitet?
unit Frm_Main;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls; type TForm1 = class(TForm) lbl_eins: TLabel; lbl_zwei: TLabel; btn_eins: TButton; btn_zwei: TButton; lbl_Zeit: TLabel; Timer1: TTimer; Timer_Eins_Start: TButton; Timer_Zwei_Start: TButton; Timer_Eins_Pause: TButton; Timer_Zwei_Pause: TButton; Timer_Eins_Resume: TButton; Timer_Zwei_Resume: TButton; Timer_Eins_Stop: TButton; Timer_Zwei_Stop: TButton; btn_EinsZwei: TButton; procedure btn_einsClick(Sender: TObject); procedure btn_zweiClick(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure Timer_Eins_StartClick(Sender: TObject); procedure Timer_Eins_ResumeClick(Sender: TObject); procedure Timer_Eins_StopClick(Sender: TObject); procedure Timer_Zwei_StartClick(Sender: TObject); procedure Timer_Eins_PauseClick(Sender: TObject); procedure Timer_Zwei_PauseClick(Sender: TObject); procedure Timer_Zwei_ResumeClick(Sender: TObject); procedure Timer_Zwei_StopClick(Sender: TObject); procedure btn_EinsZweiClick(Sender: TObject); procedure FormShow(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private { Private-Deklarationen } public { Public-Deklarationen } //Alles im Main-Thread procedure Timer_Eins; procedure Timer_Zwei; //Wird vom MyThread aufgerufen procedure Write_Counter_Eins(Counter_Eins: integer); procedure Write_Counter_Zwei (Counter_Zwei: integer); procedure Write_Uhr (Zeit: TTime); end; TMyThread_Eins = class(TThread) public procedure Execute; override; end; TMyThread_Zwei = class(TThread) public procedure Execute; override; end; TMyThread_Uhr = class(TThread) public procedure Execute; override; end; var Form1: TForm1; TH_Eins: TMyThread_Eins; TH_Zwei: TMyThread_Zwei; TH_Uhr : TMyThread_Uhr; implementation {$R *.dfm} procedure TForm1.btn_einsClick(Sender: TObject); begin Timer_Eins; end; procedure TForm1.btn_zweiClick(Sender: TObject); begin Timer_Zwei; end; procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin //TH_Eins.Free; //TH_Zwei.Free; TH_Uhr.Free; end; procedure TForm1.FormShow(Sender: TObject); begin TH_Uhr := TMyThread_Uhr.Create(False); end; procedure TForm1.btn_EinsZweiClick(Sender: TObject); begin TH_Eins := TMyThread_Eins.Create(False); TH_Zwei := TMyThread_Zwei.Create(False); end; procedure TForm1.Timer1Timer(Sender: TObject); begin lbl_Zeit.Caption := TimeToStr(now()); end; procedure TForm1.Timer_Eins; var I: integer; sEins: integer; c_Eins: integer; begin sEins := 1000; c_Eins := 0; lbl_eins.Caption := IntToStr(c_Eins); lbl_eins.Refresh; for I := 0 to 9 do begin sleep(sEins); INC(c_Eins); lbl_eins.Caption := IntToStr(c_Eins); lbl_eins.Refresh; end; end; procedure TForm1.Timer_Zwei; var I: integer; sZwei: integer; c_Zwei: integer; begin sZwei := 1000; c_Zwei := 0; lbl_zwei.Caption := IntToStr(c_Zwei); lbl_zwei.Refresh; for I := 0 to 9 do begin sleep(sZwei); INC(c_Zwei); lbl_zwei.Caption := IntToStr(c_Zwei); lbl_zwei.Refresh; end; end; procedure TForm1.Timer_Eins_PauseClick(Sender: TObject); begin if not TH_Eins.Terminated then begin TH_Eins.Suspend; end; end; procedure TForm1.Timer_Eins_ResumeClick(Sender: TObject); begin if not TH_Eins.Terminated then begin TH_Eins.Resume; end; end; procedure TForm1.Timer_Eins_StartClick(Sender: TObject); begin TH_Eins := TMyThread_Eins.Create(False); end; procedure TForm1.Timer_Eins_StopClick(Sender: TObject); begin TH_Eins.Terminate; end; procedure TForm1.Timer_Zwei_PauseClick(Sender: TObject); begin if not TH_Zwei.Terminated then begin TH_Zwei.Suspend; end; end; procedure TForm1.Timer_Zwei_ResumeClick(Sender: TObject); begin if not TH_Zwei.Terminated then begin TH_Zwei.Resume; end; end; procedure TForm1.Timer_Zwei_StartClick(Sender: TObject); begin TH_Zwei := TMyThread_Zwei.Create(False); end; procedure TForm1.Timer_Zwei_StopClick(Sender: TObject); begin TH_Zwei.Terminate; end; procedure TForm1.Write_Counter_Eins(Counter_Eins: integer); begin lbl_eins.Caption := IntToStr(Counter_Eins); lbl_eins.Refresh; end; procedure TForm1.Write_Counter_Zwei(Counter_Zwei: integer); begin lbl_zwei.Caption := IntToStr(Counter_Zwei); lbl_zwei.Refresh; end; procedure TForm1.Write_Uhr(Zeit: TTime); begin lbl_Zeit.Caption := TimeToStr(Zeit); lbl_Zeit.Refresh; end; { TMyTread_Eins } procedure TMyThread_Eins.Execute; var I: integer; sEins: integer; c_Eins: integer; begin sEins := 1000; c_Eins := 0; Synchronize(procedure begin Form1.Write_Counter_Eins(c_Eins); end); for I := 0 to 19 do begin if Terminated then begin TH_Eins.Free; Break; end; sleep(sEins); INC(c_Eins); if Terminated then begin TH_Eins.Free; Break; end; Synchronize(procedure begin Form1.Write_Counter_Eins(c_Eins); end); end; end; { TMyThread_Zwei } procedure TMyThread_Zwei.Execute; var I: integer; sZwei: integer; c_Zwei: integer; begin sZwei := 1000; c_Zwei := 0; Synchronize(procedure begin Form1.Write_Counter_Zwei(c_Zwei); end); for I := 0 to 19 do begin if Terminated then begin TH_Zwei.Free; Break; end; sleep(sZwei); INC(c_Zwei); if Terminated then begin TH_Zwei.Free; Break; end; Synchronize(procedure begin Form1.Write_Counter_Zwei(c_Zwei); end); end; end; { TMyThread_Uhr } procedure TMyThread_Uhr.Execute; var I: integer; sUhr: integer; begin sUhr := 1000; while not Terminated do begin sleep(sUhr); Synchronize(procedure begin Form1.Write_Uhr(Now()); end); end; end; end. Die zweite Frage bezieht sich auf Threads, die zur Laufzeit erzeugt werden: Wie kann ich einen Thread ansprechen, der zur Laufzeit erzeugt wurde? Ich habe das in dem kleinen Programm gelöst, indem ich mit globalen Variablen glöst habe. Aber ich habe keine Lösung gefunden, wie ich einen Thread, der zur Laufzeit ansprechen und z.B. pausieren oder stoppen kann. Vielen Dank Patrick |
AW: Erste Schritte Multi-Threading
Liste der Anhänge anzeigen (Anzahl: 1)
Moin...8-)
Ich fange mal an... Zitat:
Zitat:
ABER: Du tauscht nur den Pointer auf den Thread IN der Variable aus. Sinnbild: 1. Tasse mit Teebeutel = Thread in der Variable 2. neuer Teebeutel soll in die Tasse = Erzeugung Thread 3. Teebeutel in der Tasse wird auf den Tisch gelegt und der neue Teebeutel kommt in die Tasse. -> beide Teebeutel existieren. :wink: Die Tasse kennt aber nur den Teebeutel in der Tasse. Zitat:
Die Lösung: Verwalte deine Threads in Listen oder Dictionaries (je nach Anwendungsfall). Dann kannst du dir den Thread aus der Liste wieder in eine Variable "laden" und kannst dann damit arbeiten. ...fertsch. :wink: ![]()
Delphi-Quellcode:
Synchronize ist ja soweit ok.
Synchronize(procedure
begin Form1.Write_Uhr(Now()); end); Für die Zukunft aber: Die Threads sollten eine eigene Unit haben. Der Thread und die Thread Unit, dürfen die Anwesenheit der Form NICHT kennen. Dem Thread muß egal sein was dann passiert. Thema Wiederverwendbarkeit und zirkuläre Aufrufe. Lösung: Im Synchronize mit Events Arbeiten. Das aufrufende Objekt, in deinem Falle die Form, nimmt das Event entgegen und macht in ihrem "Bereich" das was zu zun ist: Write_Uhr(Now) Beispiel:
Delphi-Quellcode:
PS: :zwinker:
Synchronize(procedure
begin if Assigned(FOnChangeTime) then begin FOnChangeTime(Self); end; end); Bitte vermeide Denglisch. Das tut an den Augen weh.
Delphi-Quellcode:
besser
TH_Eins: TMyThread_Eins;
Delphi-Quellcode:
Bitte keine Unterstriche sondern CamelCase
THOne: TMyThreadOne;
... ![]() Bitte gebe deinen Componenten, auch zum Testen, von Anfang an sprechende Namen. Form1 ist :kotz: |
AW: Erste Schritte Multi-Threading
Das mit dem Teebeutel geht auch anders auszugrücken.
Neuen Thread erzeugen und in die Tasse hängen Teebeutel holen (Thread erstellen) Teebeutel in Tasse hängen + Schnurr um den Hänkel wickeln (Zeiger der Variable zuweisen) nochmal einen Teebeutel holen (Thread erstellen) Teebeutel in Tasse hängen + Schnurr um den Hänkel wickeln (Zeiger der Variable zuweisen) -> alter Zeiger hat in Variable keinen Platz und wird überschrieben = Schurr abschneiden und neue Schnurr drumwickeln -> Beutel bleibt aber in der Tasse (du beendest ja den alten Thread nicht) |
AW: Erste Schritte Multi-Threading
Cup, Tea and bag.... ?!!!! :?
Leave that stuff to the English, replace them with cars and car equipment's, though i remember Uwe love car examples, or wait... may be he did hate them :stupid: :duck: , can't remember now, all i have that there is some emotional connection between Uwe and cars. |
AW: Erste Schritte Multi-Threading
Hallo Zusammen,
vielen Dank für Eure Erklärungen und Hinweise! Ich habe versucht diese umzusetzen (außer DENGLISCH, das habe ich jetzt nicht angefasst ;-)), bin aber trotz intensiver Suche an ziemlich allen Fronten gescheitert. Ich habe dann versucht schrittweise vorzugehen und die Threads erst einmal in eine eigene Unit auszulagern. Weil es mir noch nicht gelungen ist, ein Event zu definieren, habe ich die Form in die uses gepackt. Die TThreadUnit
Delphi-Quellcode:
Und dann die Aufrufe in der Form:
unit TMyThreadUnit;
interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms; type TMyThreads = class strict protected private public constructor Create; end; TMyThread_Eins = class(TThread) public procedure Execute; override; end; TMyThread_Zwei = class(TThread) public procedure Execute; override; end; TMyThread_Uhr = class(TThread) public procedure Execute; override; end; var MyThreads: TMyThreads; TH_Eins: TMyThread_Eins; TH_Zwei: TMyThread_Zwei; TH_Uhr : TMyThread_Uhr; { TMyThreads } implementation uses Frm_Main; { TMyThreads } constructor TMyThreads.Create; begin end; { TMyTread_Eins } procedure TMyThread_Eins.Execute; var I: integer; sEins: integer; c_Eins: integer; begin sEins := 1000; c_Eins := 0; Synchronize(procedure begin Form1.Write_Counter_Eins(c_Eins); end); for I := 0 to 19 do begin if Terminated then begin TH_Eins.Free; Break; end; sleep(sEins); INC(c_Eins); if Terminated then begin TH_Eins.Free; Break; end; Synchronize(procedure begin Form1.Write_Counter_Eins(c_Eins); end); end; end; { TMyThread_Zwei } procedure TMyThread_Zwei.Execute; var I: integer; sZwei: integer; c_Zwei: integer; begin sZwei := 1000; c_Zwei := 0; Synchronize(procedure begin Form1.Write_Counter_Zwei(c_Zwei); end); for I := 0 to 19 do begin if Terminated then begin TH_Zwei.Free; Break; end; sleep(sZwei); INC(c_Zwei); if Terminated then begin TH_Zwei.Free; Break; end; Synchronize(procedure begin Form1.Write_Counter_Zwei(c_Zwei); end); end; end; { TMyThread_Uhr } procedure TMyThread_Uhr.Execute; var I: integer; sUhr: integer; begin sUhr := 1000; while not Terminated do begin sleep(sUhr); Synchronize(procedure begin Form1.Write_Uhr(Now()); end); end; end; end.
Delphi-Quellcode:
Aber ich mache anscheinend grundlegende Fehler in dem Aufbau der ThreadUnit. Ich kann das Programm so nicht kompilieren, weil TH_Eins.Terminated von der Form aus nicht geprüft werden kann.
unit Frm_Main;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, TMyThreadUnit; type TForm1 = class(TForm) lbl_eins: TLabel; lbl_zwei: TLabel; btn_eins: TButton; btn_zwei: TButton; lbl_Zeit: TLabel; Timer1: TTimer; Timer_Eins_Start: TButton; Timer_Zwei_Start: TButton; Timer_Eins_Pause: TButton; Timer_Zwei_Pause: TButton; Timer_Eins_Resume: TButton; Timer_Zwei_Resume: TButton; Timer_Eins_Stop: TButton; Timer_Zwei_Stop: TButton; btn_EinsZwei: TButton; procedure btn_einsClick(Sender: TObject); procedure btn_zweiClick(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure Timer_Eins_StartClick(Sender: TObject); procedure Timer_Eins_ResumeClick(Sender: TObject); procedure Timer_Eins_StopClick(Sender: TObject); procedure Timer_Zwei_StartClick(Sender: TObject); procedure Timer_Eins_PauseClick(Sender: TObject); procedure Timer_Zwei_PauseClick(Sender: TObject); procedure Timer_Zwei_ResumeClick(Sender: TObject); procedure Timer_Zwei_StopClick(Sender: TObject); procedure btn_EinsZweiClick(Sender: TObject); procedure FormShow(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private { Private-Deklarationen } public { Public-Deklarationen } //Alles im Main-Thread procedure Timer_Eins; procedure Timer_Zwei; //Wird vom MyThread aufgerufen procedure Write_Counter_Eins(Counter_Eins: integer); procedure Write_Counter_Zwei (Counter_Zwei: integer); procedure Write_Uhr (Zeit: TTime); end; var Form1: TForm1; implementation //uses TMyThreadUnit; {$R *.dfm} procedure TForm1.btn_einsClick(Sender: TObject); begin Timer_Eins; end; procedure TForm1.btn_zweiClick(Sender: TObject); begin Timer_Zwei; end; procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin TH_Uhr.Free; end; procedure TForm1.FormShow(Sender: TObject); begin TH_Uhr := TMyThread_Uhr.Create(False); end; procedure TForm1.btn_EinsZweiClick(Sender: TObject); begin TH_Eins := TMyThread_Eins.Create(False); TH_Zwei := TMyThread_Zwei.Create(False); end; procedure TForm1.Timer1Timer(Sender: TObject); begin lbl_Zeit.Caption := TimeToStr(now()); end; procedure TForm1.Timer_Eins; var I: integer; sEins: integer; c_Eins: integer; begin sEins := 1000; c_Eins := 0; lbl_eins.Caption := IntToStr(c_Eins); lbl_eins.Refresh; for I := 0 to 9 do begin sleep(sEins); INC(c_Eins); lbl_eins.Caption := IntToStr(c_Eins); lbl_eins.Refresh; end; end; procedure TForm1.Timer_Zwei; var I: integer; sZwei: integer; c_Zwei: integer; begin sZwei := 1000; c_Zwei := 0; lbl_zwei.Caption := IntToStr(c_Zwei); lbl_zwei.Refresh; for I := 0 to 9 do begin sleep(sZwei); INC(c_Zwei); lbl_zwei.Caption := IntToStr(c_Zwei); lbl_zwei.Refresh; end; end; procedure TForm1.Timer_Eins_PauseClick(Sender: TObject); begin if not TH_Eins.Terminated then begin //Auf protected Symbol TThread.Terminated kann nicht zugegriffen werden TH_Eins.Suspend; end; end; procedure TForm1.Timer_Eins_ResumeClick(Sender: TObject); begin if not TH_Eins.Terminated then begin //Auf protected Symbol TThread.Terminated kann nicht zugegriffen werden TMyThreadUnit.TH_Eins.Resume; end; end; procedure TForm1.Timer_Eins_StartClick(Sender: TObject); begin TH_Eins := TMyThread_Eins.Create(False); end; procedure TForm1.Timer_Eins_StopClick(Sender: TObject); begin TH_Eins.Terminate; end; procedure TForm1.Timer_Zwei_PauseClick(Sender: TObject); begin if not TH_Zwei.Terminated then begin //Auf protected Symbol TThread.Terminated kann nicht zugegriffen werden TH_Zwei.Suspend; end; end; procedure TForm1.Timer_Zwei_ResumeClick(Sender: TObject); begin if not TH_Zwei.Terminated then begin //Auf protected Symbol TThread.Terminated kann nicht zugegriffen werden TH_Zwei.Resume; end; end; procedure TForm1.Timer_Zwei_StartClick(Sender: TObject); begin TH_Zwei := TMyThread_Zwei.Create(False); end; procedure TForm1.Timer_Zwei_StopClick(Sender: TObject); begin TH_Zwei.Terminate; end; procedure TForm1.Write_Counter_Eins(Counter_Eins: integer); begin lbl_eins.Caption := IntToStr(Counter_Eins); lbl_eins.Refresh; end; procedure TForm1.Write_Counter_Zwei(Counter_Zwei: integer); begin lbl_zwei.Caption := IntToStr(Counter_Zwei); lbl_zwei.Refresh; end; procedure TForm1.Write_Uhr(Zeit: TTime); begin lbl_Zeit.Caption := TimeToStr(Zeit); lbl_Zeit.Refresh; end; end. Daher meine Fragen: 1. Wie muss ich die ThreadUnit aufbauen? Ich muss ja für jeden Thread einen eigenen Typen definieren... 2. Hat jemand vielleicht ein einfaches Beispiel, wie ich das mit dem Event lösen muss? Vielen Dank Patrick |
AW: Erste Schritte Multi-Threading
Zu Events hilft eventuell als Einstieg die...Hilfe:
![]() |
AW: Erste Schritte Multi-Threading
Hallo Sherlock,
vielen Dank für den Link. Leider läuft der ins Leere... Hast Du noch einen funktionierenden? Vielen Dank Patrick |
AW: Erste Schritte Multi-Threading
Zitat:
![]() |
AW: Erste Schritte Multi-Threading
Vielen Dank!
Jetzt funktioniert der Link! Kann mir noch jemand einen Tip geben, wie ich die Thread-Unit aufbauen sollte? Da steckt noch der Wurm drin... LG Patrick |
AW: Erste Schritte Multi-Threading
Zitat:
[URL] selbst drumrumschreiben, oder den Link-Button benutzen. Leider sind automatische Links zu doof, einfach die ) mitzunehmen, wenn im Link eine ( drin ist. |
AW: Erste Schritte Multi-Threading
Wenn du im Projekt eine neue Unit hinzufügst dann hast du die Möglichkeit eine neue Unit von Type Thread zu wählen. Dann ist die Unit vor formatiert.
|
AW: Erste Schritte Multi-Threading
Vielen Dank, dass wusste ich noch nicht.
Ich habe die Auslagerung gestern noch mit einer eigenen Unit gelöst, werde ich nachher posten. Kurze Frage zu den Events: Sollten diese auch in einer eigenen Unit definiert werden oder im der Form, in der sie zur Anwendung kommen sollen? Ich habe mich an den folgenden Links entlanggehangelt: ![]() und bei diesem Thema an Post 2 ![]() Das ist bis jetzt dabei rausgekommen:
Delphi-Quellcode:
In meiner ThreadUnit muss ich jetzt aber irgendwie den Aufruf anstoßen:
type
TTimeEvent = procedure (TimeValue: TTime) of object; TTimeObject = class private FTimeValue: TTimeEvent; public property OnGetTime: TTimeEvent read FTimeValue write FTimeValue; { Löst das Ereignis aus, wenn etwas registriert ist } procedure TriggerTimeEvent(TimeValue: TTime); end; TForm1 = class(TForm) lbl_eins: TLabel; lbl_zwei: TLabel; ... private { Private-Deklarationen } MyThread: TMyThreads; ... var Form1: TForm1; TimeObject: TTimeObject; implementation {$R *.dfm} { TTimeObject } procedure TTimeObject.TriggerTimeEvent(TimeValue: TTime); begin if Assigned(FTimeValue) then begin FTimeValue(TimeValue); end; end; procedure TForm1.FormCreate(Sender: TObject); begin MyThread := TMyThreads.Create; TimeObject := TTimeObject.Create; TimeObject.OnGetTime := Write_Uhr; end; procedure TForm1.FormShow(Sender: TObject); begin MyThread.TH_Uhr_Start(False); end;
Delphi-Quellcode:
Und da weiß ich nicht, wie ich den Bezug zu Form1 loswerde...
{ TMyThread_Uhr }
procedure TMyThread_Uhr.Execute; var I: integer; sUhr: integer; begin sUhr := 1000; while not Terminated do begin sleep(sUhr); Synchronize(procedure begin Form1.Write_Uhr(Now()); end); end; end; Vielen Dank Patrick |
AW: Erste Schritte Multi-Threading
Zitat:
![]() |
AW: Erste Schritte Multi-Threading
Als Parameter dem Contructor (oder anschließend einen Property/Setter) mitgeben
und intern speichern (Feld/Variable in der ThreadKlasse). aber Event/Callback ist besser (unabhängiger). Oder statt der Form-Instanz ein Interface deklarieren, in der Form implementieren, dieses als Parameter übergeben und intern speichern. Schon kann man auch bei Anderem dieses Interface implementieren und diese Threadklasse, ebenso wie beim Event/Callback, auch für Anderes wiederverwenden, da es nicht direkt mit "dieser" Form verknubbelt ist. In TComponent ist ein IInterface als Basis implementiert, ohne Referenzzählung, damit es mit der Speicherverwaltung der VCL nicht kollidiert. (sowie auch FMX usw.) |
AW: Erste Schritte Multi-Threading
Bei Events und Threads tuhe ich mich auch noch schwer.
Das Problem ist das der Thread die Form nicht kennen soll aber das Event der Form und dem Thread bekannt sein soll. Ich habe mir mit einer dritten Unit beholfen die beiden bekannt ist. In der habe ich das Event angelegt. Ich glaube es gibt eine Lösing mit Pointer und dem @ Zeichen und eine schwer verständliche Interface Lösung. |
AW: Erste Schritte Multi-Threading
z.B. dort, wo der Thread dekaliert ist, oder in einer gemeinsam genutzten Unit wird eine Event-Signatur deklariert
Delphi-Quellcode:
type TMyEvent = procedure(.....) of object;
Das als Parameter, Setter-Funktion oder Property in die ThreadKlasse. Und schon kann die Form eine "gleich" aufgebaute Objekt-Methode an das übergeben. Entsprechend dem
Delphi-Quellcode:
OnGetTime := Write_Uhr
und
Delphi-Quellcode:
if Assigned(FTimeValue) then
FTimeValue(TimeValue); |
AW: Erste Schritte Multi-Threading
Mit hat folgender Link sehr geholfen (obwohl ich nicht alles verstanden / umgesetzt habe):
![]() |
AW: Erste Schritte Multi-Threading
Hallo Zusammen,
das ganze Thema ist für mich dünnes Eis, daher habe ich mich seit Wochen schwergetan, es umzusetzen. Diese Lösung scheint zu arbeiten, aber möglicherweise sagt Ihr mir gleich, wo ich "einbrechen" werde... Ich habe es jetzt ohne Events und Callbacks realisiert und der Sync-Procedure eine Procedure aus dem MainForm gegeben... In der Thread-Unit habe ich einen Typen definiert und der Threadklasse TMyThread_Uhr eine Variable und eine Property:
Delphi-Quellcode:
Die Synchronisierung habe ich dann so vorgenommen:
type
TWriteClockValue = procedure (TimeValue: TTime) of Object; TMyThread_Uhr = class(TThread) private fWriteClockValue: TWriteClockValue; public procedure Execute; override; property WriteClockValue: TWriteClockValue read fWriteClockValue write fWriteClockValue; end;
Delphi-Quellcode:
In der aufrufenden Form sieht das dann so aus:
procedure TMyThread_Uhr.Execute;
var I: integer; sUhr: integer; O: TObject; T: TTime; begin sUhr := 1000; while not Terminated do begin sleep(sUhr); Synchronize(procedure begin if Assigned(fWriteClockValue) then fWriteClockValue(Now()); end); end; end;
Delphi-Quellcode:
Die Procedure Write_Uhr ist ein dem Formular so definiert:
procedure TfrmMain.FormShow(Sender: TObject);
begin MyThread.TH_Uhr_Start(True); MyThread.TH_Uhr.WriteClockValue := Write_Uhr; MyThread.TH_Uhr.Resume; end;
Delphi-Quellcode:
procedure TfrmMain.Write_Uhr(Zeit: TTime);
begin lbl_Zeit.Caption := TimeToStr(Zeit); lbl_Zeit.Refresh; end; Ich erstelle den Thread, pausiere ihn, weise die Procedure Write_Uhr aus der Form der Variablen in dem Thread zu und lasse den Thread dann laufen. Das scheint so zu funktionieren. Ist das eine anständige Lösung? Hier der gesamte Code der kleinen Test-App Meine Thread-Unit
Delphi-Quellcode:
Meine aufrufendes Formular
unit TMyThreadUnit;
interface uses Windows, Messages, SysUtils, Classes; type TWriteClockValue = procedure (TimeValue: TTime) of Object; TWriteTHEinsValue = procedure (THValue: integer) of Object; TWriteTHZweiValue = procedure (THValue: integer) of Object; TMyThread_Eins = class(TThread) private fWriteTHEinsValue: TWriteTHEinsValue; public procedure Execute; override; property WriteTHEinsValue: TWriteTHEinsValue read fWriteTHEinsValue write fWriteTHEinsValue; end; TMyThread_Zwei = class(TThread) private fWriteTHZweiValue: TWriteTHZweiValue; public procedure Execute; override; property WriteTHZweiValue: TWriteTHZweiValue read fWriteTHZweiValue write fWriteTHZweiValue; end; TMyThread_Uhr = class(TThread) private fWriteClockValue: TWriteClockValue; public procedure Execute; override; property WriteClockValue: TWriteClockValue read fWriteClockValue write fWriteClockValue; end; TMyThreads = class strict protected private fTH_Eins: TMyThread_Eins; fTH_Zwei: TMyThread_Zwei; fTH_Uhr : TMyThread_Uhr; public constructor Create; property TH_Eins: TMyThread_Eins read fTH_Eins write fTH_Eins; property TH_Zwei: TMyThread_Zwei read fTH_Zwei write fTH_Zwei; property TH_Uhr : TMyThread_Uhr read fTH_Uhr write fTH_Uhr; procedure TH_Eins_Start(breaked: boolean); procedure TH_Eins_Break; procedure TH_Eins_Resume; procedure TH_Eins_Stop; procedure TH_Zwei_Start (breaked: boolean); procedure TH_Zwei_Break; procedure TH_Zwei_Resume; procedure TH_Zwei_Stop; procedure TH_Uhr_Start (breaked: boolean); procedure TH_Uhr_Stop; end; var MyThreads: TMyThreads; implementation { TMyThreads } constructor TMyThreads.Create; begin end; //TH_Eins procedure TMyThreads.TH_Eins_Start(breaked: boolean); begin fTH_Eins := TMyThread_Eins.Create(breaked); end; procedure TMyThreads.TH_Eins_Break; begin if not fTH_Eins.Terminated then begin fTH_Eins.Suspend; end; end; procedure TMyThreads.TH_Eins_Resume; begin if not fTH_Eins.Terminated then begin fTH_Eins.Resume; end; end; procedure TMyThreads.TH_Eins_Stop; begin if assigned(fTH_Eins) then begin if not fTH_Eins.Terminated then begin fTH_Eins.Terminate; end; end; end; //TH_Zwei procedure TMyThreads.TH_Zwei_Start(breaked: boolean); begin fTH_Zwei := TMyThread_Zwei.Create(breaked); end; procedure TMyThreads.TH_Zwei_Break; begin if not fTH_Zwei.Terminated then begin fTH_Zwei.Suspend; end; end; procedure TMyThreads.TH_Zwei_Resume; begin if not fTH_Zwei.Terminated then begin fTH_Zwei.Resume; end; end; procedure TMyThreads.TH_Zwei_Stop; begin if assigned(fTH_Zwei) then begin if not fTH_Zwei.Terminated then begin fTH_Zwei.Terminate; end; end; end; //Uhr procedure TMyThreads.TH_Uhr_Start(breaked: boolean); begin fTH_Uhr := TMyThread_Uhr.Create(breaked); end; procedure TMyThreads.TH_Uhr_Stop; begin if assigned(fTH_Uhr) then begin if not fTH_Uhr.Terminated then begin fTH_Uhr.Terminate; end; end; end; { TMyTread_Eins } procedure TMyThread_Eins.Execute; var I: integer; sEins: integer; c_Eins: integer; begin sEins := 1000; c_Eins := 0; Synchronize(procedure begin if Assigned(fWriteTHEinsValue) then fWriteTHEinsValue(c_Eins); end); for I := 0 to 19 do begin if Terminated then begin Self.Free; Break; end; sleep(sEins); INC(c_Eins); if Terminated then begin Self.Free; Break; end; Synchronize(procedure begin if Assigned(fWriteTHEinsValue) then fWriteTHEinsValue(c_Eins); end); end; end; { TMyThread_Zwei } procedure TMyThread_Zwei.Execute; var I: integer; sZwei: integer; c_Zwei: integer; begin sZwei := 1000; c_Zwei := 0; Synchronize(procedure begin if Assigned(fWriteTHZweiValue) then fWriteTHZweiValue(c_Zwei); end); for I := 0 to 19 do begin if Terminated then begin Self.Free; Break; end; sleep(sZwei); INC(c_Zwei); if Terminated then begin Self.Free; Break; end; Synchronize(procedure begin if Assigned(fWriteTHZweiValue) then fWriteTHZweiValue(c_Zwei); end); end; end; { TMyThread_Uhr } procedure TMyThread_Uhr.Execute; var I: integer; sUhr: integer; O: TObject; T: TTime; begin sUhr := 1000; while not Terminated do begin sleep(sUhr); Synchronize(procedure begin if Assigned(fWriteClockValue) then fWriteClockValue(Now()); end); end; end; end.
Delphi-Quellcode:
unit Frm_Main;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, TMyThreadUnit; type TfrmMain = class(TForm) lbl_eins: TLabel; lbl_zwei: TLabel; btn_eins: TButton; btn_zwei: TButton; lbl_Zeit: TLabel; Timer1: TTimer; Timer_Eins_Start: TButton; Timer_Zwei_Start: TButton; Timer_Eins_Pause: TButton; Timer_Zwei_Pause: TButton; Timer_Eins_Resume: TButton; Timer_Zwei_Resume: TButton; Timer_Eins_Stop: TButton; Timer_Zwei_Stop: TButton; btn_EinsZwei: TButton; pnl_MultiThreads: TPanel; pnl_MainThread: TPanel; pnl_Results: TPanel; procedure btn_einsClick(Sender: TObject); procedure btn_zweiClick(Sender: TObject); procedure Timer1Timer(Sender: TObject); procedure Timer_Eins_StartClick(Sender: TObject); procedure Timer_Eins_PauseClick(Sender: TObject); procedure Timer_Eins_ResumeClick(Sender: TObject); procedure Timer_Eins_StopClick(Sender: TObject); procedure Timer_Zwei_StartClick(Sender: TObject); procedure Timer_Zwei_PauseClick(Sender: TObject); procedure Timer_Zwei_ResumeClick(Sender: TObject); procedure Timer_Zwei_StopClick(Sender: TObject); procedure btn_EinsZweiClick(Sender: TObject); procedure FormShow(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private-Deklarationen } MyThread: TMyThreads; public { Public-Deklarationen } //Alles im Main-Thread procedure Timer_Eins; procedure Timer_Zwei; //Wird vom MyThread aufgerufen procedure Write_Counter_Eins(Counter_Eins: integer); procedure Write_Counter_Zwei (Counter_Zwei: integer); procedure Write_Uhr (Zeit: TTime); end; var frmMain: TfrmMain; implementation {$R *.dfm} //Alles im MainThread procedure TfrmMain.Timer_Eins; var I: integer; sEins: integer; c_Eins: integer; begin sEins := 1000; c_Eins := 0; lbl_eins.Caption := IntToStr(c_Eins); lbl_eins.Refresh; for I := 0 to 9 do begin sleep(sEins); INC(c_Eins); lbl_eins.Caption := IntToStr(c_Eins); lbl_eins.Refresh; end; end; procedure TfrmMain.Timer_Zwei; var I: integer; sZwei: integer; c_Zwei: integer; begin sZwei := 1000; c_Zwei := 0; lbl_zwei.Caption := IntToStr(c_Zwei); lbl_zwei.Refresh; for I := 0 to 9 do begin sleep(sZwei); INC(c_Zwei); lbl_zwei.Caption := IntToStr(c_Zwei); lbl_zwei.Refresh; end; end; procedure TfrmMain.Timer1Timer(Sender: TObject); begin //lbl_Zeit.Caption := TimeToStr(now()); end; //OnCreate, OnShow, OnDestroy Proceduren procedure TfrmMain.FormCreate(Sender: TObject); begin MyThread := TMyThreads.Create; end; procedure TfrmMain.FormShow(Sender: TObject); begin MyThread.TH_Uhr_Start(True); MyThread.TH_Uhr.WriteClockValue := Write_Uhr; MyThread.TH_Uhr.Resume; end; procedure TfrmMain.FormDestroy(Sender: TObject); begin MyThread.TH_Uhr_Stop; MyThread.TH_Eins_Stop; MyThread.TH_Zwei_Stop; MyThread.Free; end; //ClickProceduren //Im MainThread procedure TfrmMain.btn_einsClick(Sender: TObject); begin Timer_Eins; end; procedure TfrmMain.btn_zweiClick(Sender: TObject); begin Timer_Zwei; end; procedure TfrmMain.btn_EinsZweiClick(Sender: TObject); begin MyThread.TH_Eins_Start(true); MyThread.TH_Eins.WriteTHEinsValue := Write_Counter_Eins; MyThread.TH_Eins.Resume; MyThread.TH_Zwei_Start(true); MyThread.TH_Zwei.WriteTHZweiValue := Write_Counter_Zwei; MyThread.TH_Zwei.Resume; end; //MultiThreads //TH_Eins Ckick procedure TfrmMain.Timer_Eins_StartClick(Sender: TObject); begin MyThread.TH_Eins_Start(true); MyThread.TH_Eins.WriteTHEinsValue := Write_Counter_Eins; MyThread.TH_Eins.Resume; end; procedure TfrmMain.Timer_Eins_PauseClick(Sender: TObject); begin MyThread.TH_Eins_Break; end; procedure TfrmMain.Timer_Eins_ResumeClick(Sender: TObject); begin MyThread.TH_Eins_Resume; end; procedure TfrmMain.Timer_Eins_StopClick(Sender: TObject); begin MyThread.TH_Eins_Stop; end; //TH_Zwei Click procedure TfrmMain.Timer_Zwei_StartClick(Sender: TObject); begin MyThread.TH_Zwei_Start(true); MyThread.TH_Zwei.WriteTHZweiValue := Write_Counter_Zwei; MyThread.TH_Zwei.Resume; end; procedure TfrmMain.Timer_Zwei_PauseClick(Sender: TObject); begin MyThread.TH_Zwei_Break; end; procedure TfrmMain.Timer_Zwei_ResumeClick(Sender: TObject); begin MyThread.TH_Zwei_Resume; end; procedure TfrmMain.Timer_Zwei_StopClick(Sender: TObject); begin MyThread.TH_Zwei_Stop; end; //Schreibproceduren procedure TfrmMain.Write_Counter_Eins(Counter_Eins: integer); begin lbl_eins.Caption := IntToStr(Counter_Eins); lbl_eins.Refresh; end; procedure TfrmMain.Write_Counter_Zwei(Counter_Zwei: integer); begin lbl_zwei.Caption := IntToStr(Counter_Zwei); lbl_zwei.Refresh; end; procedure TfrmMain.Write_Uhr(Zeit: TTime); begin lbl_Zeit.Caption := TimeToStr(Zeit); lbl_Zeit.Refresh; end; end. |
AW: Erste Schritte Multi-Threading
Ohne mir das alles genauer angeschaut zu haben, würde ich im
Delphi-Quellcode:
das Self.Free entfernen.
for I := 0 to 19 do begin
if Terminated then begin Self.Free; Break; end; Wenn du erreichen willst, das sich der Thread selbst frei gibt, dann verwende
Delphi-Quellcode:
FreeOnTerminate := True;
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:04 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz