Delphi-PRAXiS
Seite 2 von 3     12 3      

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


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:03 Uhr.
Seite 2 von 3     12 3      

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