Delphi-PRAXiS
Seite 2 von 3     12 3      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Verständnisfrage zur Verwendung von TMessageManager im Thread (https://www.delphipraxis.net/186032-verstaendnisfrage-zur-verwendung-von-tmessagemanager-im-thread.html)

TiGü 29. Jul 2015 13:25

AW: Verständnisfrage zur Verwendung von TMessageManager im Thread
 
Hm, stimmt...das die Werte noch an der Stelle im Speicher liegen, kann keiner garantieren.

Wie macht man es richtig?
Einen Zwischenspeicher nehmen, also einen weiteren Stack/Liste als Membervariable in der eigenen TThread-Ableitung, und dort dann reinkopieren?

Mavarik 29. Jul 2015 13:27

AW: Verständnisfrage zur Verwendung von TMessageManager im Thread
 
s.o.

TiGü 29. Jul 2015 13:32

AW: Verständnisfrage zur Verwendung von TMessageManager im Thread
 
Zitat:

Zitat von Mavarik (Beitrag 1310149)
So geht der Q-Trick

Delphi-Quellcode:

  Procedure QWas(ADateTime : TDateTime);
  var
    LDateTime : TDateTime;
  begin
    LDateTime := ADateTeim;
    TThread.Queue(NIL,Procedure
     begin
       MachWasMit(LDateTime);
     end;
  end;

...
  QWas(DateTime);
...

Verflucht, wie einfach hinterher immer so eine Lösung aussieht! Danke Mav! :thumb:
So ist das jetzt eine runde Sache.

Delphi-Quellcode:
procedure TMessageThread.SendDateTime(const ADateTime : TDateTime);
var
  LDateTime : TDateTime;
begin
  LDateTime := ADateTime;
  TThread.Queue(nil,
    procedure
    begin
      DoSendMessage(LDateTime);
    end);
end;

procedure TMessageThread.DoInternalExecute;
begin
  while not Terminated do
  begin
    try
      FLock.Enter;
      try
        if FStack.Count >= 25 then
        begin
          if not Terminated then
          begin
            while FStack.Count <> 0 do
            begin
              SendDateTime(FStack.Pop);
            end;
          end;
        end;
      finally
        FLock.Leave;
      end;
    finally
      Sleep(100);
    end;
  end;
end;

procedure TMessageThread.DoSendMessage(const ADateTime : TDateTime);
var
  LMessage : TDateTimeMessage;
begin
  LMessage := TDateTimeMessage.Create(ADateTime);
  TMessageManager.DefaultManager.SendMessage(Self, LMessage, True);
end;

Mavarik 29. Jul 2015 13:41

AW: Verständnisfrage zur Verwendung von TMessageManager im Thread
 
Abgesehen davon, dass ich keinen Thread nehmen würde...

Weil die Message ruft Dich ja auf... Es gibt also keinen Grund zu "Pollen"

TiGü 29. Jul 2015 13:48

AW: Verständnisfrage zur Verwendung von TMessageManager im Thread
 
Ja, das Beispiel ist natürlich sinnbefreit, weil hier Daten aus dem Main-Thread in den anderen Thread und wieder zurückgeschaufelt werden.
Aber mir gings ja um die grundsätzliche Heransgehensweise.
Über den TMessageManger können ja ganz andere Daten an einen Thread übergeben werden, der zum Beispiel nach N-Werten anfängt darauf eine lange Operation auszuführen und das Ergebnis wieder zurück an den Main-Thread zu übergeben.

Sir Rufo 29. Jul 2015 14:02

AW: Verständnisfrage zur Verwendung von TMessageManager im Thread
 
Dann spendier dem Thread doch wenigstens einen Event damit der nicht immer wie doof herumrödelt auch wenn es nichts zu tun gibt.

Mavarik 29. Jul 2015 14:02

AW: Verständnisfrage zur Verwendung von TMessageManager im Thread
 
Zitat:

Zitat von TiGü (Beitrag 1310158)
Ja, das Beispiel ist natürlich sinnbefreit, weil hier Daten aus dem Main-Thread in den anderen Thread und wieder zurückgeschaufelt werden.
Aber mir gings ja um die grundsätzliche Heransgehensweise.
Über den TMessageManger können ja ganz andere Daten an einen Thread übergeben werden, der zum Beispiel nach N-Werten anfängt darauf eine lange Operation auszuführen und das Ergebnis wieder zurück an den Main-Thread zu übergeben.

Das würde ich auf jeden Fall anders machen...

Delphi-Quellcode:
procedure TMyThread.Execute;
begin
  while not(Terminated) do
    begin
      try
        E_Event.WaitFor(INFINITE);
   
        if Terminated then
          exit;

        MyExecute; // Hier findet die Eigentliche Verarbeitung statt.
      except
      end;
    end;
end;

...
  FSaveID := TMessageManager.DefaultManager.SubscribeToMessage(TWhatever,
                 Procedure(Const Sender:TObject;Const M:TFMXMessage)
                   begin
                     FVerarbeite := TWhatEver(M).Value.Daten;
                     E_Event.SetEvent;
                   end);
...
Tests ob der Thread noch läuft usw.. habe ich mir jetzt gespart...

TiGü 29. Jul 2015 14:47

AW: Verständnisfrage zur Verwendung von TMessageManager im Thread
 
Soweit in Ordnung?
Ich löse das Event und damit die Verarbeitung aus, wenn der Thread mehr als 25 Items erhalten hat.
Da ich die Instanz von TEvent im Formular erzeuge und dem TThread per Konstruktor übergebe, habe ich mir die Möglichkeit offen gelassen, ggf. auch aus dem Formular heraus das Event zu setzen.
Kann man das so machen?

Delphi-Quellcode:
unit Messagner.View;

interface

uses
  System.SysUtils, System.Classes, System.Types,
  System.Messaging, System.SyncObjs, System.Generics.Collections,
  Vcl.Controls, Vcl.Forms, Vcl.StdCtrls;

type
  TIdleMessage = class(System.Messaging.TMessage)
  end;

  TDateTimeMessage = class(System.Messaging.TMessage<TDateTime>)
  end;

  TMessageThread = class(TThread)
  private
    FLock : TCriticalSection;
    FStack : TStack<TDateTime>;
    FEvent : TEvent;

    procedure GetIdleMessage(const Sender : TObject; const M : TMessage);
    procedure DoSendMessage(const ADateTime : TDateTime);
    procedure DoInternalExecute;
    procedure SendDateTime(const ADateTime : TDateTime);
  protected
    procedure Execute; override;
    procedure TerminatedSet; override;
  public
    procedure BeforeDestruction; override;
    constructor Create(const AEvent : TEvent);
    destructor Destroy; override;
  end;

  TForm1 = class(TForm)
    mmoLog : TMemo;
    procedure FormCreate(Sender : TObject);
    procedure FormDestroy(Sender : TObject);
  private
    FMessageThread : TMessageThread;
    FIdleMessage : TIdleMessage;
    FEvent: TEvent;
    procedure ThreadTerminated(Sender : TObject);
    procedure OnNewDateTimeMessage(const Sender : TObject; const M : TMessage);
    procedure LogToMemo(const Text : string);
  public
    procedure DoIdle(Sender : TObject; var Done : Boolean);
  end;

var
  Form1 : TForm1;

implementation

{$R *.dfm}


procedure TForm1.LogToMemo(const Text : string);
begin
  mmoLog.Lines.Add(Text);
end;

procedure TForm1.OnNewDateTimeMessage(const Sender : TObject; const M : TMessage);
var
  LMessage : TDateTimeMessage;
begin
  LMessage := M as TDateTimeMessage;
  LogToMemo('- - - > ' + FormatDateTime('hh:mm:ss:zzz', LMessage.Value));
end;

procedure TForm1.DoIdle(Sender : TObject; var Done : Boolean);
begin
  TMessageManager.DefaultManager.SendMessage(Self, FIdleMessage, False);
end;

procedure TForm1.FormCreate(Sender : TObject);
begin
  FIdleMessage := TIdleMessage.Create;
  Vcl.Forms.Application.OnIdle := DoIdle;
  FEvent := TEvent.Create();
  FMessageThread := TMessageThread.Create(FEvent);
  FMessageThread.OnTerminate := ThreadTerminated;
  TMessageManager.DefaultManager.SubscribeToMessage(TIdleMessage, FMessageThread.GetIdleMessage);
  TMessageManager.DefaultManager.SubscribeToMessage(TDateTimeMessage, OnNewDateTimeMessage);
end;

procedure TForm1.ThreadTerminated(Sender : TObject);
var
  LException : Exception;
begin
  TMessageManager.DefaultManager.Unsubscribe(TIdleMessage, FMessageThread.GetIdleMessage);
  if Sender is TThread then
  begin
    if TThread(Sender).FatalException is Exception then
    begin
      LException := Exception(TThread(Sender).FatalException);
      LogToMemo(LException.ToString + ' ' + LException.Message);
    end;
  end;
end;

procedure TForm1.FormDestroy(Sender : TObject);
begin
  FMessageThread.Free;
  FIdleMessage.Free;
  FEvent.Free;
end;

{ TMessageThread }

procedure TMessageThread.BeforeDestruction;
begin
  FStack.Free;
  inherited;
end;

constructor TMessageThread.Create(const AEvent : TEvent);
begin
  inherited Create;
  FEvent := AEvent;
  NameThreadForDebugging('Message-Thread');
  FLock := TCriticalSection.Create;
  FStack := TStack<TDateTime>.Create;
end;

procedure TMessageThread.SendDateTime(const ADateTime : TDateTime);
var
  LDateTime : TDateTime;
begin
  LDateTime := ADateTime;
  TThread.Queue(nil,
    procedure
    begin
      DoSendMessage(LDateTime);
    end);
end;

procedure TMessageThread.TerminatedSet;
begin
  inherited;
  FEvent.SetEvent;
end;

destructor TMessageThread.Destroy;
begin
  inherited;
  FLock.Free;
end;

procedure TMessageThread.DoInternalExecute;
begin
  FLock.Enter;
  try
    if FStack.Count >= 25 then
    begin
      if not Terminated then
      begin
        while FStack.Count <> 0 do
        begin
          SendDateTime(FStack.Pop);
        end;
      end;
    end;
  finally
    FLock.Leave;
  end;
end;

procedure TMessageThread.DoSendMessage(const ADateTime : TDateTime);
var
  LMessage : TDateTimeMessage;
begin
  LMessage := TDateTimeMessage.Create(ADateTime);
  TMessageManager.DefaultManager.SendMessage(Self, LMessage, True);
end;

procedure TMessageThread.Execute;
var
  WaitResult : TWaitResult;
begin
  inherited;
  while not Terminated do
  begin
    WaitResult := FEvent.WaitFor();
    if WaitResult = TWaitResult.wrSignaled then
    begin
      if not Terminated then
      begin
        DoInternalExecute;
      end;
    end;
  end;
end;

procedure TMessageThread.GetIdleMessage(const Sender : TObject; const M : TMessage);
var
  NowDateTime, LastDateTime : TDateTime;
begin
  FLock.Enter;
  try
    NowDateTime := System.SysUtils.Now;

    if FStack.Count <> 0 then
    begin
      LastDateTime := FStack.Peek;
      if LastDateTime <> NowDateTime then
      begin
        FStack.Push(NowDateTime);

        if FStack.Count >= 25 then
        begin
          FEvent.SetEvent;
        end;
      end;
    end
    else
    begin
      FStack.Push(NowDateTime);
    end;
  finally
    FLock.Leave;
  end;
end;

end.

Mavarik 29. Jul 2015 16:33

AW: Verständnisfrage zur Verwendung von TMessageManager im Thread
 
Zitat:

Zitat von TiGü (Beitrag 1310180)
Soweit in Ordnung?
Ich löse das Event und damit die Verarbeitung aus, wenn der Thread mehr als 25 Items erhalten hat.
Da ich die Instanz von TEvent im Formular erzeuge und dem TThread per Konstruktor übergebe, habe ich mir die Möglichkeit offen gelassen, ggf. auch aus dem Formular heraus das Event zu setzen.
Kann man das so machen?

Abgesehen davon, das die Events in umgekehrter Reihenfolge raus kommen, Du immer noch nicht die ID's speicherst die Du für den Unsubscripe brachst... Schon OK...

TiGü 30. Jul 2015 08:16

AW: Verständnisfrage zur Verwendung von TMessageManager im Thread
 
Zitat:

Zitat von Mavarik (Beitrag 1310206)
Zitat:

Zitat von TiGü (Beitrag 1310180)
Kann man das so machen?

Abgesehen davon, das die Events in umgekehrter Reihenfolge raus kommen, Du immer noch nicht die ID's speicherst die Du für den Unsubscripe brachst... Schon OK...

Das mit der Reihenfolge ist egal, aber die IDs von
Delphi-Quellcode:
SubscribeToMessage
zu speichern ist doch nur optional?!
Unsubscribe hat doch noch zwei weitere Überladungen (http://docwiki.embarcadero.com/Libra...er.Unsubscribe).
Bei einer relativ geringen Menge an
Delphi-Quellcode:
Subscribers
pro
Delphi-Quellcode:
MessageClass
ist die Iteration über die Liste doch stark vernachlässigbar oder täusche ich mich?


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