AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Algorithmen, Datenstrukturen und Klassendesign Verständnisfrage zur Verwendung von TMessageManager im Thread
Thema durchsuchen
Ansicht
Themen-Optionen

Verständnisfrage zur Verwendung von TMessageManager im Thread

Ein Thema von TiGü · begonnen am 29. Jul 2015 · letzter Beitrag vom 30. Jul 2015
 
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.071 Beiträge
 
Delphi 10.4 Sydney
 
#1

Verständnisfrage zur Verwendung von TMessageManager im Thread

  Alt 29. Jul 2015, 10:17
Um ein bisschen mit den System.Messaging.TMessageManager zu spielen, habe ich mir eine kleine Beispielapplikation in XE7 geschrieben.
Es ist natürlich ein akademisches und an den Haaren herbeigezogendes Beispiel.

Das VCL-Formular versendet im Application.OnIdle eine selbstdefinierte Nachricht namens TIdleMessage.
Diese Nachricht wird in einen externen Thread empfangen und die Uhrzeit des Empfangs mittels System.SysUtils.Now in einem Container (TQueue<TDateTime>) gespeichert.

Im TThread.Execute wird nach 25 gesammelten TDateTimes, die Werte zurück an das Formular geschickt.
Hier sende ich per TThread.Queue, damit nichts blockiert, eine weitere Nachricht (System.Messaging.TMessage<TDateTime>).

Folgendes Verständnisproblem:
Ich beobachte im Empfangshandler (OnNewDateTimeMessage) des Formulars, das ich mehrfach die gleiche Instanz und damit den gleichen Wert von TDateTime erhalte (siehe Memo-Ausgabe).
Warum ist das so?
Liegt das am TMessageManager selbst oder gehe ich falsch mit TThread.Queue um?

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;
    FQueue : TQueue<TDateTime>;

    procedure GetIdleMessage(const Sender : TObject; const M : TMessage);
    procedure DoSendMessage(const ADateTime : TDateTime);
    procedure DoInternalExecute;
  protected
    procedure Execute; override;
  public
    procedure BeforeDestruction; override;
    constructor Create;
  end;

  TForm1 = class(TForm)
    mmoLog : TMemo;
    procedure FormCreate(Sender : TObject);
    procedure FormDestroy(Sender : TObject);
  private
    FMessageThread : TMessageThread;
    FIdleMessage : TIdleMessage;
    FMessage : TDateTimeMessage;
    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;
  if FMessage <> LMessage then
  begin
    FMessage := LMessage;
    LogToMemo(sLineBreak + '- - - > ' + FormatDateTime('hh:mm:ss:zzz', FMessage.Value) + sLineBreak);
  end
  else
  begin
    LogToMemo('An der Stelle erhalte ich mehrmals die gleiche Instanz der gesendeten Nachricht.' + sLineBreak + 'Warum ist das so?')
  end;
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;
  FMessageThread := TMessageThread.Create;
  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;
end;

{ TMessageThread }

procedure TMessageThread.BeforeDestruction;
begin
  FQueue.Free;
  FLock.Free;
  inherited;
end;

constructor TMessageThread.Create;
begin
  inherited Create;
  NameThreadForDebugging('Message-Thread');
  FLock := TCriticalSection.Create;
  FQueue := TQueue<TDateTime>.Create;
end;

procedure TMessageThread.DoInternalExecute;
var
  LDateTime : TDateTime;
begin
  while not Terminated do
  begin
    FLock.Enter;
    try
      if FQueue.Count >= 25 then
      begin
        while FQueue.Count > 0 do
        begin
          if not Terminated then
          begin
            LDateTime := FQueue.Dequeue;

            Queue(
              procedure
              begin
                DoSendMessage(LDateTime);
              end);
          end;
        end;
      end;
    finally
      FLock.Leave;
    end;
  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;
begin
  inherited;
  DoInternalExecute;
end;

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

    if FQueue.Count <> 0 then
    begin
      if FQueue.Peek <> LDateTime then
      begin
        FQueue.Enqueue(LDateTime);
      end;
    end
    else
    begin
      FQueue.Enqueue(LDateTime);
    end;
  finally
    FLock.Leave;
  end;
end;

end.
  Mit Zitat antworten Zitat
 


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:57 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 by Thomas Breitkreuz