Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Daten vom Thread zur Klasse zur Anwendung (https://www.delphipraxis.net/149293-daten-vom-thread-zur-klasse-zur-anwendung.html)

moelski 18. Mär 2010 08:50


Daten vom Thread zur Klasse zur Anwendung
 
Moin !

Ich versuche mich gerade an etwas Theorie zum Thema Threads, Klassen und Notifikationen. Und dabei bin ich auf ein paar Dinge gestoßen wo ich im Moment nicht weiter komme.

Zu meiner Idee ...
Nehmen wir an wir wollen eine Anwendung erstellen die von mehreren Eingangsquellen Daten aufnehmen kann. Und gehen wir mal weiter davon aus das ein Eingang ein TCP Socket ist und ein Eingang z.B. eine CSV Datei.
Um das ganze möglichst universell zu halten ist meine Idee nun, die Eingänge jeweils in eine Klasse zu packen. Wir erstellen also zwei Klassen die in etwa so aussehen könnten:
Delphi-Quellcode:
type
  TInput = class(TPersistent)
    private
      Daten    : string;
      InpThread : TInputThread;
    protected

    public
      constructor Create;
      destructor Destroy; override;
    published

  end;
InpThread ist an dieser Stelle nur ein "Platzhalter" der mittels Thread Daten liefern kann. Hier könnte letztlich alles mögliche stehen. Sei es ein TCP Server, eine serielle Schnittstelle, ein Thread der eine CSV auswertet, ....

Der Thread schaut im Moment mal so aus:
Delphi-Quellcode:
type
  TInputThread = CLASS(TThread)
    private
      Daten : string;
    protected
      procedure execute; override;
    public
      constructor create; virtual;
  end;

procedure TInputThread.execute;
begin
//  while not Terminated do;
  Daten := 'test';
end;

constructor TInputThread.create;
begin
  inherited create(true); // CreateSuspended = true
  freeOnTerminate := true;
end;
Nun frage ich mich aber ... Wie kann ich vom Thread die Daten zur Klasse bekommen? Kann man da eine Art Notifikation absetzen?
Und wie kann ich dann aus der Klasse eine Notifikation in Richtung Anwendung schicken um zu signalisieren das neue Daten zur Bearbeitung zur Verfügung stehen?

Hat da jemand eine gute Idee zu?

ghost007 18. Mär 2010 09:01

Re: Daten vom Thread zur Klasse zur Anwendung
 
Naja, du musst das "Ereginis" im Prinzip zur Hauptanwendung durchreichen. Egtl. musst du dir nur eine Procedure baun, die als Notification fungiert und dann darauf reagieren. Verstehe dein Problem jetzt nicht so ganz. Ich würde dem Thread eine variable geben, in die du beim erstellen des Threads die Notify Procedure übergibst. Die kann der Thread dann aufrufen und das Hauptprogramm weiß bescheid.

moelski 18. Mär 2010 09:09

Re: Daten vom Thread zur Klasse zur Anwendung
 
Moin !

Mein Thread ist aber Bestandteil der Klasse.
Wie kommen dann die Daten vom Thread zur Klasse? Der Thread selber "kennt" die Klasse ja gar nicht.

ghost007 18. Mär 2010 09:35

Re: Daten vom Thread zur Klasse zur Anwendung
 
Hey,
wie schon gesagt, ich würde dem Thread eine Variable geben, in der du eine Procedure speicherst. Die Procedure wird dann vom Thread aufgerufen, wenn er Daten loswerden will. Die Procedure selber liebt im Hauptprogramm. Natürlich musst du diese Procedure in einer CritialSection aufrufen, da du sonst Probleme mit inkonsistenten Daten bekommen kannst.

//Edit: Natürlich kann die besagte Procedure auch einfach nur in der Klasse liegen, anstatt im Hauptprogramm

Blup 18. Mär 2010 09:44

Re: Daten vom Thread zur Klasse zur Anwendung
 
Delphi-Quellcode:
interface

uses
  Classes;

type
  TInputMethode = procedure(AValue: string) of object;

  TInputThread = class(TThread)
  private
    FDaten : string;
    FOnInput: TInputMethode;
  protected
    procedure DoOnInput;
    procedure Execute; override;
  public
    constructor create; virtual;
    property OnInput: TInputMethode read FOnInput write FOnInput;
  end;

  TInput = class(TPersistent)
  private
    FDaten    : string;
    FInpThread : TInputThread;
  protected
    procedure DoInput(AValue: string);
  public
    constructor Create;
    destructor Destroy; override;
  published
  end;

implementation

{ TInputThread }

procedure TInputThread.DoOnInput;
begin
  if Assigned(FOnInput) then
    FOnInput(FDaten);
end;

procedure TInputThread.execute;
begin
//  while not Terminated do;
  FDaten := 'test';
  Synchronize(DoOnInput);
end;

constructor TInputThread.create;
begin
  inherited create(true); // CreateSuspended = true
  // problematisch da TInput eine Zeiger auf den Thread besitzt
  // freeOnTerminate := true;
  freeOnTerminate := False;
end;

{ TInput }

constructor TInput.Create;
begin
  inherited;
  FInpThread := TInputThread.Create;
  FInpThread.OnInput := DoInput;
  FInpThread.Resume;
end;

destructor TInput.Destroy;
begin
  FInpThread.Free;
  inherited;
end;

procedure TInput.DoInput(AValue: string);
begin
  FDaten := AValue;
  {...}
end;

ghost007 18. Mär 2010 10:00

Re: Daten vom Thread zur Klasse zur Anwendung
 
Hi Blup,
bist du dir sicher, dass der Synchronize call auf die DoOnInput, den Zugriff auf die Property FDaten thread-safe macht?
Ich glaub nämlich nicht. So wie ich das verstanden habe, bewirkt synchronize ja nur, dass die Procedure nur sequentiell aufgerufen werden kann. Das schützt die Variable aber nicht vor Änderungen.

guinnes 18. Mär 2010 10:28

Re: Daten vom Thread zur Klasse zur Anwendung
 
Syncronize läuft im Hauptthread und da der NebenThread darauf wartet, das Syncronize abgearbeitet wird, kann er die Daten nicht ändern

ghost007 18. Mär 2010 10:32

Re: Daten vom Thread zur Klasse zur Anwendung
 
Zitat:

Zitat von guinnes
Syncronize läuft im Hauptthread und da der NebenThread darauf wartet, das Syncronize abgearbeitet wird, kann er die Daten nicht ändern

Hm, dann hab ich das wohl mit java verwechselt... dachte das Synchronize bezieht sich nur auf die jeweilige Klasse.

moelski 18. Mär 2010 10:32

Re: Daten vom Thread zur Klasse zur Anwendung
 
Moin !

Zitat:

Syncronize läuft im Hauptthread
Genau das stellt aber ein Problem dar. Wenn nämlich der Hauptthread (das wäre im aktuellen Fall ja die Anwendung selber) hängt, dann würde es auch den ganzen Rest blocken und es würde mitunter sogar zum Datenverlust führen.

Gibt es nicht evtl. eine Variante um ganz auf Synchronize zu verzichten?
Derzeit nutzen wir Windows Messages um den Thread von der Anwendung zu entkoppeln und die Daten zu übergeben.
So wie es auch hier beschrieben wird: http://edn.embarcadero.com/article/22411

Nur leider kann man an Klassen ja keine Messages schicken.

ghost007 18. Mär 2010 10:37

Re: Daten vom Thread zur Klasse zur Anwendung
 
Naja, zum übergeben der Daten MUSST du in irgend einer Form synchronisieren. Entweder über das besagte Synchronize oder eine CriticalSection.

moelski 18. Mär 2010 10:46

Re: Daten vom Thread zur Klasse zur Anwendung
 
Könnte ich nicht auch die Klasse TInput in einem Thread erzeugen?

Also folgendes Konstrukt:
Thread -> TInput -> TInputThread

Dann wäre ich doch das komplette Gebilde abgekapselt von meinem MainThread.
Und ich müsste mich dann nur noch drum kümmern wie ich den ersten Thread mit meiner Anwendung Synce. Aber das könnte ich über die Windows Message Methode lösen.

:gruebel:

mirage228 18. Mär 2010 10:49

Re: Daten vom Thread zur Klasse zur Anwendung
 
Man kann schon eine Message an eine Klasse schicken. Diese muss lediglich ein Fenster besitzen, das Windows Messages empfangen kann, was Du aber erzeugen kannst :)

moelski 18. Mär 2010 10:55

Re: Daten vom Thread zur Klasse zur Anwendung
 
Moin !

Aber kann ja nicht die Lösung sein jeder Klasse ein Fenster mitzugeben (was ansonsten komplett ungenutzt wäre) nur um Windows Messages zu empfangen. Irgendwie widerstrbt mir dieser Gedanke :)

ghost007 18. Mär 2010 11:05

Re: Daten vom Thread zur Klasse zur Anwendung
 
Zitat:

Zitat von moelski
Moin !

Aber kann ja nicht die Lösung sein jeder Klasse ein Fenster mitzugeben (was ansonsten komplett ungenutzt wäre) nur um Windows Messages zu empfangen. Irgendwie widerstrbt mir dieser Gedanke :)

Hmm, theoretisch könntest du es auch über lokale sockets machen :D wenn das dir besser gefällt.

//Edit: Hätte den Vorteil, dass du die "Reader-Threads" später auch mal als eigenständiges Programm machen kannst, wenn es die Performance verlangt.

Blup 18. Mär 2010 14:17

Re: Daten vom Thread zur Klasse zur Anwendung
 
Delphi-Quellcode:
interface

uses
  Classes, SyncObjs;

type
  TInputMethode = procedure(AValue: string) of object;

  TStringPuffer = class(TCriticalSection)
    constructor Create;
    destructor Destroy; override;
  private
    FList: TStringList;
  public
    function Count: Integer;
    procedure Put(AValue: string);
    function Get: string;
  end;

  TInputThread = class(TThread)
    constructor Create(APuffer: TStringPuffer);
  private
    FPuffer: TStringPuffer;
  protected
    procedure Execute; override;
  end;

  TInput = class(TPersistent)
    constructor Create;
    destructor Destroy; override;
  private
    FInpThread: TInputThread;
    FPuffer: TStringPuffer;
  public
    property Puffer: TStringPuffer read FPuffer;
  end;

implementation

{ TInputThread }

procedure TInputThread.Execute;
var
  Daten: string;
begin
//  while not Terminated do;
  Daten := 'test';
  FPuffer.Put(Daten);
end;

constructor TInputThread.create(APuffer: TStringPuffer);
begin
  inherited create(False); // CreateSuspended = False
  freeOnTerminate := False;
end;

{ TInput }

constructor TInput.Create;
begin
  inherited;
  FPuffer := TStringPuffer.Create;
  FInpThread := TInputThread.Create(FPuffer);
end;

destructor TInput.Destroy;
begin
  FInpThread.Free;
  FPuffer.Free;
  inherited;
end;

{ TStringPuffer }

function TStringPuffer.Count: Integer;
begin
  Enter;
  try
    Result := FList.Count;
  finally
    Leave;
  end;
end;

constructor TStringPuffer.Create;
begin
  inherited;
  FList := TStringList.Create;
end;

destructor TStringPuffer.Destroy;
begin
  FList.Free;
  inherited;
end;

function TStringPuffer.Get: string;
begin
  Enter;
  try
    if FList.Count = 0 then
      Result := ''
    else
    begin
      Result := FList[0];
      FList.Delete(0);
    end;
  finally
    Leave;
  end;
end;

procedure TStringPuffer.Put(AValue: string);
begin
  Enter;
  try
    FList.Add(AValue);
  finally
    Leave;
  end;
end;
Der Hauptthread muss dann halt hin und wieder schaun ob neue Daten da sind.
Alternativ könnte der Puffer auch eine Nachricht per Post versenden, wenn neue Daten eintreffen.
Dafür habe ich hier im Forom schon mal eine Lösung geschrieben, also schau mal dort nach.


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:29 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