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 |
AW: Wie erzeugt man ein Event?
http://www.michael-puff.de/Programmi...reignis1.shtml
Aber man kann auch mit Nachrichten arbeiten. Ist eventuell einfacher. |
AW: Wie erzeugt man ein Event?
Deklaration eines EventHandlers:
Delphi-Quellcode:
Wobei man bei Events eines Threads (meistens) darauf achten sollte, dass man diese synchronisiert auslöst.
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;
Delphi-Quellcode:
procedure TMyThread.Execute;
begin ... Synchronize(DoEvent); ... end; procedure TMyThread.DoEvent; begin if Assigned(FOnEvent) then FOnEvent(<Parameter>); end; |
AW: Wie erzeugt man ein Event?
Danke. Hab es nun wie folgt programmiert.
Im der Main-Unit:
Delphi-Quellcode:
und dann in der Thread-Unit:
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;
Delphi-Quellcode:
Scheint zu klappen. :-D
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; |
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 |
AW: Wie erzeugt man ein Event?
Zitat:
|
AW: Wie erzeugt man ein Event?
Zitat:
|
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:
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:
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;
Delphi-Quellcode:
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.
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; |
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. |
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. |
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