![]() |
Thread - Synchronize - 2 Forms - Verständnisproblem
Hallo,
ich habe schon bei Google und in diversen Foren gesucht, und auch Luckies Tutorial gelesen und die Demos angeschaut, aber irgendwie bekomme ich es nicht auf die Reihe :( Zum Problem: Ich habe zwei Forms. Beim Erzeugen von Form1 wird Form2 miterzeugt. Form1 wird angezeigt, Form2 soll noch nicht angezeigt werden. Nachdem Form1 angezeigt worden ist, soll aus Form1 heraus ein Thread erzeugt werden, der eine Methode aus Form2 ausführt. Form2.Methode greift dabei auf VCL-Objekte zu; aber nur auf welche, die auch auf Form2 liegen. Also auf ihre eigenen und nicht auf welche aus Form1! Nach einem Klick auf einen Button auf Form1 soll dann Form2 angezeigt werden. Dabei bekomme ich dann den Fehler auf ein ungültiges Fensterhandle... Ich weiß, dass man bei Zugriffen auf die VCL aus Threads heraus Synchronize benutzen soll. Das verstehe ich auch insoweit, kann es nur nicht auf meine Situation anwenden. Ich verstehe Synchronize so: Aus Form1 wird ein Thread in Form2 aufgerufen, der auf VCL-Objekte in Form1 zugreifen soll. Also ruft der Thread in Form2 mittels Synchronize eine Methode in Form1 auf, die den Zugriff auf das Objekt regelt. In meinem Fall soll doch aber Form2 nur auf Objekte zugreifen, die auch auf ihr selbst liegen. Ich vestehe nicht, wie ich da Synchronize nutzen soll. Ist es nicht unsinnig, die Objekte, die auf Form2 liegen, in Form1 zu erzeugen? Ich habe hier anscheinend eine echte Denkblokade, und würde mich freuen, wenn Ihr mir auf die Sprünge helfen könnt. Vielen Dank! Form1:
Delphi-Quellcode:
Form2:
Procedure LoadData;
Begin Form2.LoadData; End; Procedure Form1.TuWas; Begin oThread := BeginThread(nil, 0, Addr(LoadData), nil, 0, iID); ResumeThread(oThread); If oThread <> 0 Then Begin CloseHandle(oThread); End; GetExitCodeThread(oThread, iExit); End;
Delphi-Quellcode:
Procedure Form2.LoadData;
Begin //mache was mit VCL-Objekten End; |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Es ist egal, ob die Form sichtbar ist oder nicht. Und es ist auch egal wo du zeichnest. Entscheidend ist, dass du beim Zeichnen (Nutzung der VCL) auf globale Ressourcen zugreifst. Und dieser Zugriff muss synchronisiert werden (leider macht es die VCL nicht von alleine).
|
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Dein Problem liegt imho daran, dass Du eine Thread-Routine mit Zugriffen auf grafische Elemente als Threadroutine übergibst. Das kann nicht klappen, da die VCL die Zugriffe im Kontext des Hauptthreads ausführt. Solche Zugriffe müssen innerhalb des Threads mit synchronize abgesichert werden.
Also musst Du solche Routinen deklarieren und da solltest Du dann eine TThread-Klasse für schaffen. Übrigens steht ein Thread immer für sich alleine, egal wo er deklariert ist. Er kann also nicht zu Form1 oder Form2 gehören, selbst wenn er zufällig in derselben Datei deklariert ist. Solltest Du den Thread als Member der Klasse TForm2 deklariert haben, könnte das die Fehlermeldung verursacht haben. Grüße, Messie |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Zitat:
Aber die Funktionen, die der Thread aufruft, zeichnen ja auf der gleichen Form, in der der Thread läuft. @Messie Und der Hauptthread ist in diesem Fall der aus Form1, also der, aus dem Form2 erzeugt wird? Würde das bedeuten, dass ich die Objekte, die sich auf Form2 befinden sollen, doch in Form1 erzeugen muss? :shock: Wie könnte soetwas denn aussehen? Ich schreibe in Form2 eine Methode, in der ich eine Methode aus Form1 aufrufe, die dann die Objekte auf Form2 erzeugt? Das ist doch von Hinten durch die Brust ins Auge. |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Zitat:
Grüße, Messie |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Wenn du die Anzeigen auf Form2 partout in einem eigenen Thread machen willst, gibt es einen kleinen Trick, der dir vielleicht hilft: Man kann ein zweites (VCL)Formular im Kontext eines Threads mit Hilfe eines zusätzlichen Application-Objekts erzeugen und im Thread selbst die Nachrichtenwarteschlange abarbeiten. Das Formular ist dann komplett losgelöst vom Hauptthread und kann dann auch im eigenen Thread seine Controls aktualisieren etc.
Du kannst dann allerdings nicht direkt vom Hauptformular aus die entsprechende Methode im zweiten Formular aufrufen. Eine passende Windows-Nachricht schafft aber Abhilfe. Hier ein Beispiel dazu : Hauptformular
Delphi-Quellcode:
Formular2 :
interface
{...} type TFormThread = class (TThread) private FApplication : TApplication; protected procedure Execute; override; public FForm : TForm2; procedure OnNotify (Sender : TObject); end; TForm1 = class(TForm) Button1: TButton; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private FFormThread : TFormThread; FTerminateByClose : Boolean; procedure OnTerminate (Sender : TObject); end; {...} implementation {...} {TForm1} procedure TForm1.FormCreate(Sender: TObject); begin FTerminateByClose := False; end; procedure TForm1.Button1Click(Sender: TObject); begin if not Assigned (FFormThread) then begin FFormThread := TFormThread.Create(True); FFormThread.FreeOnTerminate := True; FFormThread.OnTerminate := OnTerminate; FFormThread.Resume; end; end; procedure TForm1.OnTerminate(Sender: TObject); begin if not FTerminateByClose then FFormThread := nil; end; procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); begin If Assigned (FFormThread) then begin FTerminateByClose := True; SendMessage (FFormThread.FForm.Handle, WM_CLOSE, 0, 0); FFormThread.FreeInstance; FFormThread := nil; end; Action := caFree; end; { TFormThread } procedure TFormThread.Execute; begin FApplication := TApplication.Create(nil); try FApplication.Initialize; FForm := TForm2.Create(FApplication); FForm.OnNotifyParent := OnNotify; FForm.Show; while (not Terminated) do begin Sleep (1); FApplication.ProcessMessages; end; FApplication.ShowHint := False; FApplication.Destroying; FApplication.DestroyComponents; finally FreeAndNil (FApplication); end; end; procedure TFormThread.OnNotify(Sender: TObject); begin Terminate; end;
Delphi-Quellcode:
Mit dem Button in Formular1 wird das zweite Formular erzeugt und angezeigt.
interface
{...} type TForm2 = class(TForm) procedure FormClose(Sender: TObject; var Action: TCloseAction); private FOnNotify : TNotifyEvent; public property OnNotifyParent : TNotifyEvent read FOnNotify write FOnNotify; end; {...} implementation {TForm2} procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction); begin Action := caHide; if Assigned (FOnNotify) then FOnNotify(Self); end; |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Ich verstehe es einfach nicht.
Ich starte nun von Form1 aus einen Thread, der eine Methode in Form2 aufruft. Diese erzeugt dann VCL-Objekte usw. Wie mache ich das denn nun mit dem Synchronize? Ich muss in Form2 mit Synchronize irgendetwas aufrufen, was woanders liegt (wo?) und mir dann die VCL-Objekte erzeugt? Die Beispiele in Luckies Demos leuchten mir ein, aber hier komme ich einfach nicht weiter :( |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Hi!
Zitat:
Delphi-Quellcode:
Mir fällt gerade auf, du erstellst den Thread ja per BeginThread. Synchronize ist eine Methode von TThread. Wenn du nicht TThread benutzen möchtest, müsstest du dir vermutlich einen eigenen Synchronisierungsmechanismus bauen.
type
MeinForm = class(TMeinForm) lblThreadText: TLabel; private procedure StarteDenThread; end; type Thread = class(TThread) private Form: TMeinForm; procedure MachVCLSachen; protected procedure Execute; override; public constructor Create(Form: TMeinForm); end; procedure MeinForm.StarteDenThread; begin Thread.Create(Self); end; constructor Thread.Create(Form: TMeinForm); begin inherited Create(False); Self.Form := Form; end; procedure Thread.Execute; begin // Kram machen Synchronize(MachVCLSachen); // Kram machen end; procedure Thread.MachVCLSachen; begin Form.lblThreadText := 'Der Thread lässt grüßen!'; end; Kleiner Nachtrag, weil du durch den Thread VCL-Objekte erzeugen möchtest: Dir ist aber schon klar, dass Synchronize die Befehle an den Haupt-Thread weiterleitet, diese *nicht* in deinem eigenen Thread ausgeführt werden, und das Form dadurch beschäftigt ist/geblockt wird, oder? Wenn du nur VCL-Dinge über den Thread erzeugen möchtest, wäre der Thread an der Stelle absolut überflüssig. Das nur noch nebenbei. |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
in deinem Beispiel müsstest du einfach Form2.Loaddata synchronisieren, was
|
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Zitat:
Grüße, Messie |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Vielen Dank an alle.
Das mit der Trennung von Anzeige und Daten ist so eine Sache, da die Anzeige ja die Daten anzeigt :gruebel: Bedeutet, ich interagiere ja in dem Moment, wo ich die Daten lade, ja schon mit der Anzeige, da das VCL-Objekt ja die Daten lädt. |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Ich nochmal.
Zitat:
|
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
@wicht,
das klingt einleuchtend! Werde das mal probieren umzusetzen. Danke nochmal an alle! |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Hallo,
darf ich auch mal eine Frage bezüglich Threads stellen? Bis dato hatte ich noch keine Threads für meine Anwendungen benötigt, bei meinem jetzigen Projekt (Lichtsteuerung) sollten/müssen mehrere dinge selbstständig laufen. Mein Problem ist das ich zur Laufzeit mehrere Frames erzeuge je nach Anzahl der Scheinwerfer. Nun versuche ich in den Frames einen Thread zu integrieren damit diese Frames selbstständig ihr Werte für die visuelle Darstellung erzeugen. Die Daten dafür sollen sie sich aus dem Hauptform aus einem record hohlen. Geht das überhaupt da die threads ja mit den Frames zu Laufzeit erzeugt werden. MfG Dirk |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Funktionalität und Darstellung sollten in einem Programm sowieso getrennt sein.
Bei Threads ist das Pflicht. Ein Beispiel wie man das in diesem Fall angehen könnte: Definiere eine eigene Nachricht: WM_STATUS = WM_USER + xxxx; Aufgabe des Hauptthreads: Vergieb für jede Aufgabe eine Nummer/ID. Erzeuge entsprechende Threads und übergib diesen ihre Aufgaben und das Handle eines Fensters (das über Statusänderungen informieren soll). Erzeuge entsprechende Frames für jede zu visualisierende Aufgabe. Aufgabe des Subthreads: Führe deine Aufgabe aus. Ändert sich der Status, informiere das angegebene Fenster: PostMessage(WindowHandle, WM_STATUS, ID, 0); Aufgabe des Hauptthreads: Wenn eine Nachricht von über eine Statusänderung eintrifft, finde auf Grund der ID das entsprechende Frame und rufe die Methode zur Aktualisierung der Darstellung auf. Statt Nachrichten könnte man auch Synchronize verwenden, das bremst aber die Subthreads aus. |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Hallo
Danke werde mich mal mit der sache beschäftigen, habe ich noch nicht gemacht. MfG |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Hallo,
mit dem ganzen Threating steige ich nicht so richtig hinter. Jetzt habe ich gnadenlos drauf losprogrammiert ohne Rücksicht auf Verluste. Neben dem Hauptprogramm habe ich mir einen Thread erstellt (ShowThread = class(TThread) mit dem ich meine Ablaufsteuerung realisiert habe. In der Ablaufsteuerung greift der Thread direkt auf einen Record (array) zu wo er sich Daten für seine Aufgabe holt und seine Ergebnisse auch wieder ablegt. Die Daten für die VLC Komponenten mache ich über Synchronize denke das ist ok. Auf die Felder auf die er seine Ergebnisse ablegt, schreibt auch kein anderer Thread Daten rein also nur ein Ausgabepuffer. Jetzt benötige ich noch einen Thread (OutputThread) der aus den Ergebnissen des Thread (ShowThread) die eigentlichen Ausgangsdaten erzeugt, muss ich da auf etwas achten (kritischer Abschnitt oder so) wenn die Daten von anderen Threads nur gelesen werden oder kann es zu Konflikten kommen? MfG Dirk |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Sobald ein Thread in einen bestimmten Speicherbereich schreibt, ist ein "kritischer Abschnitt" erforderlich, wenn andere Threads ebenfalls lesend oder schreibend auf diesen Speicherbereich zugreifen sollen.
|
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Hallo an alle,
ich bin wahrscheinlich zu bl..... :wall: Ich steige da nich hinter, habe zwar ein wenig gelesen aber das was ich will ist nirgens nur ansatzweise zu finden. Habe mir mal erlaub ein Stück Quellcode reinzusetzen.
Delphi-Quellcode:
Es funktioniert zwar alles störungsfrei aber ich denke das es nicht richtig ist, wäre sonst zu
unit ShowThreadPars;
interface uses Windows, Messages, Classes, SysUtils, Forms, Variants, Graphics, Controls, Dialogs, StdCtrls, ExtCtrls, ComCtrls, ToolWin, ParFrame; type ShowThread = class(TThread) procedure SetData(Value:Byte); procedure ParsOut; function SetColor(Value:Byte; index:Byte): TColor; protected procedure Execute; override; end; Var tParsindex : Byte; // Rückgabewert tTrackbarValue : Byte; // Rückgabewert tLabelValue : String; // Rückgabewert tShapeColor : Tcolor; // Rückgabewert tShapeValue : Byte; tDmxAdr : Byte; tDMXValue: Byte; tFrames : Integer; tProgRunOld : array [1..32] of Byte; tProgRunTemp : array [1..2,1..32] of Byte; tProgStepTemp : Byte = 1 ; implementation uses ProLightControlForm; // ----------------------------------------------------------------------------- procedure ShowThread.Execute; Var I : Byte; begin // stopen des thread durch hauptprogramm while fThreadParExit do begin ------------------------------------------------------------------------------------- 'PARMAX' ist eine Variabel aus dem Hauptprogramm die sich aber jederzeit ändern kann auch wärend der Tread läuft ------------------------------------------------------------------------------------- // hauptschleife für alle parser For i:= 1 to ParMax do begin . . . ------------------------------------------------------------------------------- HIER GREIFE ICH AUF VARIABLE IM HAUPTTREAD ZU UM SIE ZU LESEN BZW. ZU SCHREIBEN ------------------------------------------------------------------------------- tDmxAdr := ParCount[i,1]; // dmx adresse des pars hohlen if fStandbyActive then begin Device[tDmxAdr].DMXP := ProgRun[ProgStep,i]; // daten in ausgabe-array zurückschreiben tProgRunOld[i] := ProgRun[ProgStep,i]; // hilfsarray für flash funktion... end else begin Device[tDmxAdr].DMXP := tProgRunOld[i];// daten aktuell halten end; . . . --------------------------------------- HIER SCHREIBEN ICH DIREKT IN DEN RECORD --------------------------------------- if Device[tDmxAdr].ParStatus = 1 Then Device[tDmxAdr].DMXP := 255; SetData(tDmxAdr); // ausgabedaten berechnen . . . ----------------------------- AKTUALISIEREN DER VCL OBJEKTE ----------------------------- if tProgRunTemp[1,tDmxAdr] <> tProgRunTemp[2,tDmxAdr] then // Ausgabe visualisierung Synchronize(ParsOut); tProgRunTemp[2,tDmxAdr]:=tProgRunTemp[1,tDmxAdr]; sleep(1); end; // end For next end; end; // ----------------------------------------------------------------------------- // end For next end; end; // ----------------------------------------------------------------------------- procedure ShowThread.ParsOut; Begin ProLightForm.Thread_Parasync(tParsindex, tTrackbarValue,tLabelValue, tShapeColor) end; // ----------------------------------------------------------------------------- procedure ShowThread.SetData(Value:Byte); Var dmxValueStr : String; begin tShapeValue := Device[tDmxAdr].ParColorSet; // Colorindex holen tDMXValue := Device[tDmxAdr].ParDmxOut; // Programmwert holen tProgRunTemp[1,tDmxAdr]:=tDMXValue; // nach speedfader . . . . einfach. Kann mir da mal jemand helfen, wie ich einen sauberen Zugriff auf Variable im Haupttread hinbekomme da die sich ständig ändernden Daten und der Tread darauf reagieren soll. MfG Dirk |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Delphi-Quellcode:
Diese ganzen globalen Variablen gehn überhaupt nicht. Eine Variable sollte immer jemanden gehören.
Var
tParsindex : Byte; // Rückgabewert tTrackbarValue : Byte; // Rückgabewert tLabelValue : String; // Rückgabewert tShapeColor : Tcolor; // Rückgabewert tShapeValue : Byte; tDmxAdr : Byte; tDMXValue: Byte; tFrames : Integer; tProgRunOld : array [1..32] of Byte; tProgRunTemp : array [1..2,1..32] of Byte; tProgStepTemp : Byte = 1 ; implementation In diesem Fall entweder einem Formular oder dem Thread und jeweils dort in den private oder protected-Abschnitt deklariert. Andere Klassen können grundsätzlich nur über property auf diese Variablen zugreifen. -----------------------------------------------------------------------------
Delphi-Quellcode:
Das gehört so nicht in eine Unit, die sich um die Funktionalität kümmert (Trennung von Funktionaliät und Oberfläche).
uses ProLightControlForm;
{...} Synchronize(ParsOut); {...} procedure ShowThread.ParsOut; Begin ProLightForm.Thread_Parasync(tParsindex, tTrackbarValue,tLabelValue, tShapeColor) end; Man kann zwei Fälle unterscheiden: 1. das Ereignis muss sofort bearbeitet und der Thread in der Zwischenzeit anhalten 2. das Ereignis kann bei nächster Gelegenheit verarbeitet werden, der Thread kann weiter arbeiten Fall 1.: In diesem Fall ist Synchronize() erforderlich. Die Methode sollte aber nicht direkt, sondern über einen Methodenzeiger(oder Interface) erfolgen.
Delphi-Quellcode:
Fall 2.:
TParasyncMethode = procedure(Sender: TObject {optional weitere Parameter}) of object;
ShowThread = class(TThread) {...} private FOnParasync: TParasyncMethode; procedure SetOnPararasync(AValue: TParasyncMethode); function GetOnPararasync: TParasyncMethode; public property OnParasync: TParasyncMethode read GetOnPararasync write SetOnPararasync; {...} procedure ShowThread.ParsOut; var lParasync: TParasyncMethode; Begin lParasync := GetOnPararasync; if Assigned(lParasync) then lParasync(Self {optional weitere Parameter}); end; Synchronize() ist nicht erforderlich. Der Hauptthread wird asynchron mit Hilfe von Nachrichten informiert. Man kann ein eigenes threadsicheres Nachrichtensystem implementieren oder man nutzt das von Windows.
Delphi-Quellcode:
-----------------------------------------------------------------------------
const
WM_Parasync = WM_User + XXX; ShowThread = class(TThread) {...} private FParasyncWnd: THandle; procedure SetPararasyncWnd(AValue: THandle); function GetPararasyncWnd: THandle; public property ParasyncWnd: THandleread GetPararasyncWnr write SetPararasyncWnd; {...} procedure ShowThread.ParsOut; var lParasyncWnd: THandle; Begin lParasyncWnd := GetParasyncWnd; if Assigned(lParasyncWnd) then PostMessage(lParasyncWnd, WM_Parasync, Integer(Pointer(Self)), 0 {Optional weiterer Parameter}); end; Threadsicherer Zugriff auf interne Variabeln eines Objekts über Getter und Setter:
Delphi-Quellcode:
Auf die interne Variable FPararasyncWnd darf in diesem Fall nirgends sonst zugegriffen werden,
private
FSection: TCriticelSection; {im Konstruktor erzeigen, im Destrucor freigeben, in der Regel wird nur eine je Objekt benötigt} {...} procedure ShowThread.SetPararasyncWnd(AValue: THandle); begin FSection.Acquire; try FPararasyncWnd := AValue; finally FSection.Release; end; end; function ShowThread.GetPararasyncWnd: THandle; begin FSection.Acquire; try Result := FPararasyncWnd; finally FSection.Release; end; end; einzige Ausnahme in einem genauso mit FSection abgesicherten Abschnitt. Der selbe Mechanismus müsste auch in alle anderen Klassen eingebaut werden, auf dessen Property der Thread zugreifen soll. Das würde ich aber vermeiden, neue Daten sollten immer vom Hauptthread and den Subtrhead übergeben oder von dort abgeholt werden. Der letzte Satz sollte eigentlich deine Frage beantworten, übergib geänderte Daten an eine so absicherte Methode. Der Thread erstellt sich eine Kopie davon und arbeitet mit dieser. Schreibfehler wurden eingefügt, um die Aufmerksamkeit des Lesers zu erhöhen. |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Hallo,
vielen Dank erstmal, ich kau das erst mal durch und muss es verdauen. Mfg Dirk |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Hallo Blub
nochmals danke für deine Ausführung. Ich habe da aber so meine Verständnissprobleme oder besser gesagt das so in meinem projekt umzusetzen. Mit der Abgrenzung der Variablen und Proceduren habe ich befolgt, leuchtet ja auch ein da sie sonst überall verfügbar sind und ich denke mal es auch konflikte mit Namensgleichheit geben kann. Ich habe ein wenig gegoogelt und versucht das mit den Variablen in einem Thread zu verstehen und es giebt da wiedersprüchliche Meinungen hinsichtlich des zugriffs auf Variablen ausserhalb eines Thread's. Manche sagen jeder Zugriff ob schreiben oder lesen muss abgesichert werden ander wiederung nur das scheiben da bei lesezugriffe keine werte verändert werden und auch nur den wenn mehrere Thread in gleiche Variable schreiben. Wie schon gesagt benutzen ich einen Record der alle informationen für die Steuerung enthält. Die dort abgespeicherten Daten werden jeweils nur von einem thread veränder, alle ander höhlen sich zu gegebener Zeit die Daten um damit wiederum ihre Berechnungen durchzuführen und legt das Ergebnis in seine eigens für sich reservierten Variable ab damit andere Thread sie sich dort wieder holen können. Klingt warscheinlich alles etwas kaotisch, aber ich habe hin und her überlegt wie ich eine Ablaufsteuerung hinbekomme, so das alle Module (Procsesse) selbstständig laufen ohne auf ein bestimmtes Ergebnis eine anderen Modul (Thread) zu warten, dass Hauptformular hat nur noch die Aufgabe auf Eingaben von aussen zu warten. Das Programm läuft zwar auf meine Rechner stabil und störungsfrei ich möchte natülich konflikte weitestgehend ausschliessen. Ich muss noch dazu sagen das ich in der Programmierung mit Delphi ein Neuling bin, bisher habe ich hauptsächlich mich mit Microcontroller beschäftigt. Hättest du eine Idee wie ich eine Rückmeldung in einen Thread machen kann wenn ein Benutzer einen Button oder eine Trackbar betätigt (bisher habe ich das auch über Variablen gemacht, geht auch) da der Thread diese informationen verarbeiten soll. Eine message denke ich geht nicht die müssten dann ja in mengen verschickt werden solange wie die Trackbar bedient wird. Die Sache mit der VCL Aktualiesierng per PostMessage gefällt mir zwar ser gut bekomme ich aber irgendwie nicht hin (Fensterhandle). Die Form für die Visualisierung werden zur Laufzeit erstellt je nach Bedarf und Menge. Auch zur Laufzeit kann sich das noch durch Benutzereingabe ändern. Giebt es eigendlich auch eine möglichkeit Message zu versenden ohne Handle wo jeder diese empfängt und je nach Parameter dann die Daten für sich in anspruch nimmt. Jetzt will ich deine Gedult mit meinen Problemen nicht überstrapazieren. MfG Dirk PS: Ich kann dir ja das Prog mal als lauffähige Variante zumailn damit du eine Vorstellung hast wass ich da treibe. Vieleicht erklären sich dann meine Probleme besser. |
Re: Thread - Synchronize - 2 Forms - Verständnisproblem
Hallo Dustin,
sobald ein Thread in eine Speicherbereich schreibt und ein anderer Thread diesen liest, muss dieser Bereich geschützt werden. Nur wenn überhaupt kein Schreibzugriff erfolgt, kann darauf verzichtet werden. Genaue Beschreibung gibts in der Delphihilfe: -> TCriticalSection -> TMultiReadExclusiveWriteSynchronizer Die Entwicklung des Anwendungsaufbaus kann ich dir nicht abnehmen, da ich die konkreten Anforderungen an dein Projekt nicht kenne. Hier ein par Anregungen: Ich würde jeden möglichen Prozessabschnitt als eigenständige Klasse erstellen. Der Prozessabschnitt sollte eine Liste von Schnittstellen bereitstellen, die Eingabeparameter entgegen nehmen oder Ausgabewerte bereitstellen. Der Abschnitt kann sich bei Bedarf einen Thread erstellen, der bestimmte Aufgaben erfüllt. Dazu werden Eingabeparameter an threadsichere Property übergeben. Auf Änderungen des Status/Ergebniss wird indirekt reagiert. Der Thread schickt eine Botschaft an das Hauptformular, daß diese an den entsprechenden Prozessabschnitt übergibt. Dieser liest den Status/Ergebniss über threadsichere Property und informiert die Ausgabeschnittstellen. Alternativ könnte der Thread diese Aufgabe übernehmen, dafür müssen die Schnittstellen aber dann threadsicher sein. Eine andere Möglichkeit, der Thread setzt bei Änderungen ein Property auf True, im Hauptthread werden über einen globalen Timer alle Prozessabschnitte zur Aktualisierung aufgefordert, der Thread reagiert auf das Property und setzt wieder False. Die Erstellung und Verknüpfung der Prozessabschnitte untereinander kann im Kontext des Gesamtprogramms interaktiv mit dem Benutzer erfolgen. Für jeden Prozessabschnitt-Klasse muss eine passendes Visualisierungsklasse existieren. Diese sollte über die Ein-und Ausgabeschnittstellen mit der Prozessklasse komunizieren und ein Steuerelement (z.B. ein Frame mit Reglern und Anzeigen) bereitstellen, steuern und Eingaben an die Prozessklasse weitergeben. |
AW: Thread - Synchronize - 2 Forms - Verständnisproblem
Hab das jetzt so gelöst jetzt funktioniert es allerdings lässt sich das fenster nicht mehr verschieben wenn der Thread am zählen ist.
Delphi-Quellcode:
type
ParseThread = class(TThread) private { Private-Deklarationen } Form1: TForm1; i:integer; protected procedure Execute; override; procedure DoSomething; public { Public-Deklarationen } constructor Create(Form1: TForm1); end; var Form1: TForm1; tt: ParseThread; implementation {$R *.dfm} procedure ParseThread.Execute; begin while not Terminated do begin Synchronize(DoSomething); end; end; procedure ParseThread.DoSomething; begin inc(i); form1.Memo1.Lines.Add(inttostr(i)); if i = 5000 then begin i:=0; SuspendThread(tt.Handle); end; end; constructor ParseThread.Create(Form1: TForm1); begin inherited Create(False); Self.Form1 := Form1; end; procedure TForm1.BitBtn1Click(Sender: TObject); begin tt := ParseThread.Create(Self); tt.FreeOnTerminate := true; end; end. |
AW: Thread - Synchronize - 2 Forms - Verständnisproblem
Delphi-Quellcode:
Damit eliminierst du den Vorteil eines Threads und kannst ihn auch gleich weg lassen.
Synchronize(DoSomething);
Hör auf den Rat den du hier im Forum bekommen hast, und arbeite ich in die Grundlagen ein. |
AW: Thread - Synchronize - 2 Forms - Verständnisproblem
Zitat:
Aber es funktioniert, ist zuverlässig und einfach - und sofern der Thread 90% seiner Arbeit außerhalb des Synchronize macht, auch kein Problem! Also ja, das ist sinnlos:
Delphi-Quellcode:
Das hingegen absolut sinnvoll und schön kurz + gut lesbar:
procedure ParseThread.Execute;
begin while not Terminated do begin Synchronize(DoSomething); end; end;
Delphi-Quellcode:
Wobei LongrunningCalculation ruhig länger dauern darf (ist ja entkoppelt vom Hauptthread) und die Ergebnisse in Feldvariablen der Klasse schreibt. Da Synchronize blockiert, sollte Zugriff auf die Feldvariablen sicher sein. (Das ist mMn auch der Grund, weshalb es blockiert: Damit man über private Felder Daten austauschen kann)
procedure ParseThread.Execute;
begin while not Terminated do begin LongrunningCalculation(); Synchronize(ShowProgress); end; end; Da
Delphi-Quellcode:
eben sehr flott ist, ist mMn das Beispiel schlecht gewählt. Eventuell ginge Primzahlen finden besser, da zeitaufwändiger ...?
inc(i)
|
AW: Thread - Synchronize - 2 Forms - Verständnisproblem
Es hat mich nicht ganz losgelassen, daher hier mal ein Beispiel das Primzahlen sucht. Ja, die Berechnung ist absichtlich langsam, wobei ich nicht schätzen kann, wie langsam :stupid: (Und ich hab das ohne IDe hingetippt, vll. sind da noch kleine Fehler drin)
Der Thread ist (bzgl. Abhängigkeiten) entkoppelt von der UI und steltl nur ein Event zur Verfügung. die UI kann (am Besten in einer anderen unit) dann das Event abonnieren und den Fortschritt anzeigen.
Delphi-Quellcode:
type
TPrimeInfoEvent = procedure(Sender: TPrimeFinderThread; number: Integer) of Object; TPrimeFinderThread = class(TThread) private testNumber: Integer; isPrime: Boolean; protected procedure Execute; override; procedure ShowProgress; function IsNumberPrime(number: Integer); fProgressCallback: TPrimeInfoEvent; public { Public-Deklarationen } constructor Create(Form1: TForm1); end; implementation constructor PrimeFinderThread.Create(callback: TPrimeInfoEvent); begin inherited Create(False); Self.fProgressCallback = callback; FreeOnTerminate := True; end; procedure PrimeFinderThread.Execute; begin testNumber = 10000; while not Terminated and testNumber < 1e7 do begin if IsNumberPrime(testNumber) then Synchronize(ShowProgress); testNumber := testNumber+1; end; end; procedure PrimeFinderThread.ShowProgress; begin fProgressCallback(Self, testNumber); end; function PrimeFinderThread.IsNumberPrime(number: Integer): Boolean; var i: Integer; divisorCount: Integer; remainder: Integer; begin divisorCount := 0; for i := 1 to number do begin remainder := number; while remainder >= i do { remainder := number mod i } begin remainder := remainder - i; end if remainder = 0 then inc(divisorCount); end Result := divisorCount = 2; end; end. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:35 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