Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Thread - Synchronize - 2 Forms - Verständnisproblem (https://www.delphipraxis.net/136472-thread-synchronize-2-forms-verstaendnisproblem.html)

Pilloker 1. Jul 2009 09:07


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:
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;
Form2:
Delphi-Quellcode:
Procedure Form2.LoadData;
Begin
  //mache was mit VCL-Objekten
End;

sirius 1. Jul 2009 09:18

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

messie 1. Jul 2009 10:13

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

Pilloker 1. Jul 2009 10:17

Re: Thread - Synchronize - 2 Forms - Verständnisproblem
 
Zitat:

Zitat von sirius
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).

Das habe ich schon verstanden.
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.

messie 1. Jul 2009 10:26

Re: Thread - Synchronize - 2 Forms - Verständnisproblem
 
Zitat:

Zitat von Pilloker
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:

Nein, der HauptThread ist der, in dem das Programm läuft. Und da gehört die Erzeugung und Verwaltung aller VCL-Forms, Events etc. dazu. Ein von Dir zusätzlich erzeugtes Thread-Objekt ist wie ein zusätzliches unabhängiges Programm, was nicht auf die VCL-Elemente Deines Hauptprogramms zugreifen kann.

Grüße, Messie

fajac 1. Jul 2009 10:28

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

Delphi-Quellcode:
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;
Mit dem Button in Formular1 wird das zweite Formular erzeugt und angezeigt.

Pilloker 1. Jul 2009 10:32

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

wicht 1. Jul 2009 10:46

Re: Thread - Synchronize - 2 Forms - Verständnisproblem
 
Hi!

Zitat:

Das habe ich schon verstanden.
Aber die Funktionen, die der Thread aufruft, zeichnen ja auf der gleichen Form, in der der Thread läuft.
Der Thread läuft in keinem Form. Deine Forms laufen im Haupt/VCL-Thread, das ist einer. Und dann gibt es noch deinen eigenen, erstellten Thread. Egal wo er deklariert ist oder gestartet wird, er läuft einfach immer neben dem VCL-Thread und neben anderen eventuell vorhandenen Threads. So könnte das, was du brauchst, aussehen (einfach mal so niedergeschrieben, ohne Test):

Delphi-Quellcode:
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;
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.

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.

sirius 1. Jul 2009 10:47

Re: Thread - Synchronize - 2 Forms - Verständnisproblem
 
in deinem Beispiel müsstest du einfach Form2.Loaddata synchronisieren, was
  1. nicht sinnvoll ist (gesamten Thread synchronisieren)
  2. nicht möglich ist, da du nicht TThread benutzt
Wenn Loaddata so lange dauert liegt es sicher nicht an der Erstellung von VCL-Objekten sondern an anderen datentechnischen Sachen. Hier gilt erstmal Daten von Anzeige zu trennen. Dann kannst du erst die Daten laden und daraufhin die Anzeige gestallten. Und das Laden kannst du in einen Thread auslagern.

messie 1. Jul 2009 10:48

Re: Thread - Synchronize - 2 Forms - Verständnisproblem
 
Zitat:

Zitat von Pilloker
Ich starte nun von Form1 aus einen Thread, der eine Methode in Form2 aufruft.

Der Thread bekommt eine abzuarbeitende Methode übergeben. Die mag in Form2 stehen, ist aber die Thread-Methode. Das ist kein Aufruf, sondern der Thread selbst. Alles andere muss in untergeordneten Routinen stehen. Werden diese mit synchronize aufgerufen, können dort auch VCL-Objekte erzeugt werden. Aber nicht im Thread selbst.

Grüße, Messie

Pilloker 1. Jul 2009 10:57

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.

wicht 1. Jul 2009 11:12

Re: Thread - Synchronize - 2 Forms - Verständnisproblem
 
Ich nochmal.

Zitat:

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.
Nein. Du interagierst (solltest du), *nachdem* du die Daten geladen hast. Nicht während dessen. Der Thread hat die Daten zu lesen/bearbeiten/was auch immer und sollte diese sich in einer Struktur merken, die einfach weiter zu verwenden ist. Zum Beispiel könnte der Thread eine Eigenschaft LoadedData haben. Und wenn der Thread das befüllt, muss er nichts synchronisieren. Sobald der Thread seine Eigensschaft LoadedData befüllt hat (Verarbeitung der Daten beendet) ruft er einmal eine Methode der Form über Synchronize auf. Die Form weißt dann ihren Controls die relevanten Sachen aus LoadedData zu. Damit ist das Laden komplett im Thread, die Anzeige der geladenen Daten komplett in der GUI. So würde das vorgehen Sinn machen (meiner Meinung nach).

Pilloker 1. Jul 2009 12:46

Re: Thread - Synchronize - 2 Forms - Verständnisproblem
 
@wicht,

das klingt einleuchtend!
Werde das mal probieren umzusetzen.

Danke nochmal an alle!

dustin 9. Jul 2009 23:19

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

Blup 10. Jul 2009 07:27

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.

dustin 11. Jul 2009 09:53

Re: Thread - Synchronize - 2 Forms - Verständnisproblem
 
Hallo

Danke werde mich mal mit der sache beschäftigen, habe ich noch nicht gemacht.

MfG

dustin 14. Jul 2009 20:27

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

Blup 15. Jul 2009 08:04

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.

dustin 22. Jul 2009 20:01

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:
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
.
.
.
.
Es funktioniert zwar alles störungsfrei aber ich denke das es nicht richtig ist, wäre sonst zu
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

Blup 23. Jul 2009 10:08

Re: Thread - Synchronize - 2 Forms - Verständnisproblem
 
Delphi-Quellcode:
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
Diese ganzen globalen Variablen gehn überhaupt nicht. Eine Variable sollte immer jemanden gehören.
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:
uses ProLightControlForm;
{...}
Synchronize(ParsOut);
{...}
procedure ShowThread.ParsOut;
Begin
ProLightForm.Thread_Parasync(tParsindex, tTrackbarValue,tLabelValue, tShapeColor)
end;
Das gehört so nicht in eine Unit, die sich um die Funktionalität kümmert (Trennung von Funktionaliät und Oberfläche).
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:
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;
Fall 2.:
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:
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;
Auf die interne Variable FPararasyncWnd darf in diesem Fall nirgends sonst zugegriffen werden,
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.

dustin 23. Jul 2009 17:03

Re: Thread - Synchronize - 2 Forms - Verständnisproblem
 
Hallo,

vielen Dank erstmal, ich kau das erst mal durch und muss es verdauen.

Mfg Dirk

dustin 27. Jul 2009 19:49

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.

Blup 28. Jul 2009 10:44

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.

Zodi 19. Apr 2020 22:06

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.

DieDolly 19. Apr 2020 22:08

AW: Thread - Synchronize - 2 Forms - Verständnisproblem
 
Delphi-Quellcode:
      Synchronize(DoSomething);
Damit eliminierst du den Vorteil eines Threads und kannst ihn auch gleich weg lassen.

Hör auf den Rat den du hier im Forum bekommen hast, und arbeite ich in die Grundlagen ein.

jfheins 20. Apr 2020 09:40

AW: Thread - Synchronize - 2 Forms - Verständnisproblem
 
Zitat:

Zitat von DieDolly (Beitrag 1462476)
Delphi-Quellcode:
      Synchronize(DoSomething);
Damit eliminierst du den Vorteil eines Threads und kannst ihn auch gleich weg lassen.

Ich lese das immer wieder, aber es stimmt einfach nicht. Ja, Synchronize blockiert den Thread während der Aktualisierung. Es macht insofern keinen Sinn, 100% der Arbeit des Threads in einer Synchronize-Methode zu machen.

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:
procedure ParseThread.Execute;
begin
 while not Terminated do
   begin
     Synchronize(DoSomething);
   end;
end;
Das hingegen absolut sinnvoll und schön kurz + gut lesbar:
Delphi-Quellcode:
procedure ParseThread.Execute;
begin
  while not Terminated do
  begin
    LongrunningCalculation();
    Synchronize(ShowProgress);
  end;
end;
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)

Da
Delphi-Quellcode:
inc(i)
eben sehr flott ist, ist mMn das Beispiel schlecht gewählt. Eventuell ginge Primzahlen finden besser, da zeitaufwändiger ...?

jfheins 20. Apr 2020 13:39

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:23 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz