Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Delphi Wie erzeugt man ein Event? (https://www.delphipraxis.net/167798-wie-erzeugt-man-ein-event.html)

UliBru 19. Apr 2012 10:04

Wie erzeugt man ein Event?
 
Ich habe in einem früheren Thread, siehe hier mal gefragt wie ich ein Datenereignis festellen kann.
Mittlerweile habe ich daraus folgendes gemacht:
In einem eigenen Thread bearbeite ich zyklisch eintreffende Daten und speichere diese in zwei wechselseitigen Puffern. Grundidee: während der eine beschrieben wird, kann der zweite verarbeitet werden. Das klappt soweit.
Nun würde ich gerne im VCL-Thread eine Ereignisprozedur verwenden, die genau dann aufgerufen wird, wenn ein Pufferwechsel stattgefunden hat, natürlich mit dem Parameter, welcher der beiden Puffer nun fürs Lesen freigegeben ist. Quasi als würde der Thread einen Button-Click auslösen, mit dem dann in der zugehörigen onClick-Methode der Lesepuffer verarbeitet wird.

Meine Fragen: wie erzeuge ich dieses Event aus dem Thread heraus? Wie sieht eine Grundstruktur hierzu aus, was muss in der Main-Unit stehen und was im Thread? Gibt es da ein einfaches verständliches Beispiel?

Uli

Luckie 19. Apr 2012 10:10

AW: Wie erzeugt man ein Event?
 
http://www.michael-puff.de/Programmi...reignis1.shtml

Aber man kann auch mit Nachrichten arbeiten. Ist eventuell einfacher.

DeddyH 19. Apr 2012 10:13

AW: Wie erzeugt man ein Event?
 
Deklaration eines EventHandlers:
Delphi-Quellcode:
type
  TMyEvent = procedure(<Parameter>) of object;

  TDings = class
  private
    FOnEvent: TMyEvent;
    ...
  public
    property OnEvent: TMyEvent read FOnEvent write FOnEvent;
    ...
  end;

implementation

procedure TDings.DoSomething;
begin
  DoWork;
  if Assigned(FOnEvent) then
    FOnEvent(<Parameter>);
end;
Wobei man bei Events eines Threads (meistens) darauf achten sollte, dass man diese synchronisiert auslöst.
Delphi-Quellcode:
procedure TMyThread.Execute;
begin
  ...
  Synchronize(DoEvent);
  ...
end;

procedure TMyThread.DoEvent;
begin
  if Assigned(FOnEvent) then
    FOnEvent(<Parameter>);
end;

UliBru 19. Apr 2012 11:25

AW: Wie erzeugt man ein Event?
 
Danke. Hab es nun wie folgt programmiert.

Im der Main-Unit:
Delphi-Quellcode:
interface

  type
  TMainForm = class(TForm)
  private
    ...
  public
    ...
    procedure OnBufferSwitch(bufidx:integer);
  end;

implementation

procedure TMainForm.OnBufferSwitch(bufidx:integer);
begin
  Label1.Caption := IntToStr(bufidx);
  DoSomething(bufidx);
end;
und dann in der Thread-Unit:
Delphi-Quellcode:
interface

type
  TOnBufferSwitch = procedure(bufidx: Integer) of object;

type
  TmyThread = class(TThread)
  private
    ...
    bufidx: integer;
    FOnEvent: TOnBufferSwitch;
  protected
    procedure Execute; override;
  public
    property OnEvent: TOnBufferSwitch read FOnEvent write FOnEvent;
  end;

implementation

procedure TVASThread.Execute;
var oldbufidx: integer;
begin
  FOnEvent := MainForm.OnBufferSwitch; //Zuweisung Ereignisprozedur
  while not(Terminated) do
  begin
    Fillbuffers; //do something
    ...
    if bufidx <> oldbufidx then
      Synchronize(SyncBufferSwitch);
    sleep(10);
  end;
end;

procedure TmyThread.SyncBufferSwitch;
begin
  if Assigned(FOnEvent) then
    FOnEvent((bufidx);
end;
Scheint zu klappen. :-D

TBx 19. Apr 2012 11:29

AW: Wie erzeugt man ein Event?
 
Schmeiß mal ganz schnell die Zuweisung des Eventhandlers aus der Execute-Methode.
Diese Zuweisung gehört dahin, wo auch das Threadobjekt created wird (ich vermute mal. Deine Main-Unit).
Ansonsten müßte ja der Thread Dein Hauptformular kennen und das widerspricht absolut der OOP.

Gruß aus dem Hohen Norden

Thom 19. Apr 2012 11:54

AW: Wie erzeugt man ein Event?
 
Zitat:

Zitat von TBx (Beitrag 1162653)
Ansonsten müßte ja der Thread Dein Hauptformular kennen und das widerspricht absolut der OOP.

:gruebel:

UliBru 19. Apr 2012 12:02

AW: Wie erzeugt man ein Event?
 
Zitat:

Zitat von TBx (Beitrag 1162653)
Schmeiß mal ganz schnell die Zuweisung des Eventhandlers aus der Execute-Methode.
Diese Zuweisung gehört dahin, wo auch das Threadobjekt created wird (ich vermute mal. Deine Main-Unit).
Ansonsten müßte ja der Thread Dein Hauptformular kennen und das widerspricht absolut der OOP.

Danke, erledigt. Hatte das Threadobject direkt mit non-suspended kreiert, daher die (falsche) Zuweisung in Execute. Man lernt eben nie aus.

Medium 19. Apr 2012 12:42

AW: Wie erzeugt man ein Event?
 
So konterkarierst du allerdings die Nebenläufigkeit, da der Thread für die Dauer die der Handler braucht ja steht, und der Buffer-Flip nicht mehr nötig wäre. Sauberer und mehr in deinem Sinne wären Fensternachrichten, die asynchron via PostMessage() möglich sind. Gerüst:

Delphi-Quellcode:
unit uDataThread;
const
  WM_BUFFERFLIP = WM_USER + 1234;

type
  TDataThread = class(TThread)
  private
    FHandlerHanlde: HWND;
    Buffer1, Buffer2, CurrentBuffer: TMyBufferType;
    Buffer1CS, Buffer2CS, CurrentCS: TCriticalSection;
  protected
    procedure Execute; override;
  public
    constructor Create(aHandlerHandle: HWND);
  end;

implementation

constructor TDataThread.Create(aHandlerHandle: HWND);
begin
  inherited Create(false);
  FHandlerHandle := aHandlerHandle;
end;

procedure TDataThread.Execute;
begin
  repeat
    CurrentCS.Enter;
    try
      // Do things with CurrentBuffer
    finally
      CurrentCS.Leave;
    end;

    if DoBufferFlip then
    begin
      PostMessage(FHandlerHandle, WM_BUFFERFLIP, Integer(CurrentBuffer), Integer(CurrentCS));
      FlipBuffersAndCriticalSections;
    end;
  until Terminated;
end;

{-------------------------------------------}
unit uMainForm;

uses
  uDataThread;

type
  TMainForm = class(TForm)
  private
    FDataThread: TDataThread;
    procedure BufferHandler(var Msg: TMessage); message WM_BUFFERFLIP;
  public
  end;

implementation

procedure TMainForm.BufferHandler(var Msg: TMessage);
begin
  TCriticalSection(Msg.WParam).Enter;
  try
    DoSomethingWithBuffer(TMyBufferType(Msg.LParam));
  finally
    TCriticalSection(Msg.WParam).Leave;
  end;
end;
Die CriticalSections sind dann wichtig, wenn der Handler länger braucht als das Befüllen eines Buffers, gehören aber auch zum "guten Ton". Ich persönlich würde sogar noch einen Schritt weiter gehen, und eine Liste von Buffern machen. Fertige werden vom Thread an diese gehängt und das MainForm via PostMessage() darüber informiert. Der Handler schnappt sich dann immer den ältesten Buffer, tut was er damit tun soll, und kümmert sich um die Freigabe sowie Löschung aus der Liste. Idealerweise schaut der dann grob so aus:
Delphi-Quellcode:
  while DataThread.BufferList.Count>0 do
  begin
    DoStuffWithBuffer(DataThread.BufferList[0]); // muss nicht synchronisiert werden, da der Thread diesen Buffer nicht mehr anfasst, und in der Liste nur hinten Added
    DataThread.ListCS.Enter;
    try
      DataThread.BufferList.Delete(0); // sollte hingegn doch gesynced werden, weil dabei Daten in der Liste verschoben werden, was sich bei einem parallelen Add() im Thread schlecht macht (TList verwendet intern Arrays, es ist keine einfache Linked-List.)
    finally
      DataThread.ListCS.Leave;
    end;
  end;
Hier muss also nur noch das Adden und Deleten in der Liste duch eine CS gesichert werden, und man könnte im Thread auch mehrere Buffer in die Liste werfen bevor man die Verarbeitung im MainForm anstößt. Zudem hat man so eine handliche Queue, die Phasen großem Buffer-Aufkommens flexibel puffert, und der Handler kann nach und nach alles abarbeiten wenn Zeit dafür ist.

UliBru 19. Apr 2012 13:38

AW: Wie erzeugt man ein Event?
 
Danke, :thumb: das sieht nicht schlecht aus. Muss ich aber erst einmal verdauen (=verstehen, realisieren, testen). Ich schau mir das mal näher an.
Vermutlich komme ich aber ohne TList aus, die Daten müssen später auch wieder zeitgerecht ausgegeben werden, da kann ich mir ein Buffer-Overrun oder -Underrun sowieso nicht leisten, dann muss das Programm stehen bleiben. Ergo muss der Handler schnell genug arbeiten. Und ich kann dann noch mit der Puffergröße angleichen.

silver-moon-2000 19. Apr 2012 14:59

AW: Wie erzeugt man ein Event?
 
Tut mir leid, wenn ich diesen Thread einfach so entere und nichts sinnvolles beisteuern kann, aber ich hätte mal dazu eine Frage:

Sowohl Luckie als auch Medium raten hier zu Messages anstelle von Events. Kann mir einer der Materie-Kundigen kurz darlegen, warum hier Messages einfacher / vorzuziehen sind?
Oder etwas allgemeiner: Was ist der Unterschied zwischen Events und Messages (mit Ausnahme der möglichen Asynchronität (mithilfe von PostMessage (im Gegensatz zu SendMessage)))?

Oder, nochmal umformuliert: Gibt es Guidelines, wann welches Konstrukt sinnvoller / besser / effektiver / wasauchimmerer einzusetzen ist, oder ist das (wie so vieles) eher dem Gusto des Programmierers überlassen?


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:56 Uhr.
Seite 1 von 2  1 2      

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