AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Eventhandler für asynchron arbeitendende Objekte
Thema durchsuchen
Ansicht
Themen-Optionen

Eventhandler für asynchron arbeitendende Objekte

Ein Thema von jensw_2000 · begonnen am 9. Jan 2008 · letzter Beitrag vom 12. Jan 2008
Antwort Antwort
jensw_2000
(Gast)

n/a Beiträge
 
#1

Eventhandler für asynchron arbeitendende Objekte

  Alt 9. Jan 2008, 09:52
Verständnisfrage:

Ich habe eine Wrapper-Klasse für ein COM Object gebaut, das asynchron arbeitet.

Vereinfacht sieht die Klasse so aus:

Delphi-Quellcode:
Type
  TCallhandler = class;

  TCallhandlerStateChangeEvent = procedure(Sender:TCallhandler; aCallstate:APICallstateEnum);
  
  TCallhandler = class(TComponent)
  private
    FCallhandlerStateChangeEvent: TCallhandlerStateChangeEvent;
    FCurrentAPICallstate:APICallstateEnum;
    FAPICallEvents :TAPICallEvents
    FAPICallObj:IDivaCall; << das COM Object aus einer API
    procedure OnAPICallstateChange(aCallstate:APICallstateEnum); // wird über ein Sink Object des COM Objectes asynchron ausgelöst
  public
    constructor ...
    destructor ...
    property Callstate:APICallstate
             read FCurrentCallstate;
    property OnCallhandlerStateChange:TCallhandlerStateChangeEvent
             read FCallhandlerStateChangeEvent write FCallhandlerStateChangeEvent;
  end;

{ TCallhandler }
procedure TCallhandler.OnCallhandlerStateChange(aCallstate:APICallstateEnum);
begin
  FCurrentAPICallstate := aCallstate;
  if assigned(FCallhandlerStateChangeEvent) then
    FCallhandlerStateChangeEvent(self,FCurrentAPICallstate);
end;
Ich habe in einem Projekt 120 Instanzen dieses Callhandlers in einer abgeleiteten ObjectList zusammengefasst.

Delphi-Quellcode:
Type
  TCallhandlerGroup = class(TObjectList)
  private
    FSumIdle:integer;
    FSumConnecting:integer;
    FSumConnected:integer;
    procedure OnCallhandlerChange(aCallhandler:TCallhandler; aCallstate:APICallstateEnum);
    procedure UpdateCounters(Sender:TObject);
    ...
  public
    procedure GetUsageCounters(var callhandlerCount,idle,connecting,connected:intger);
    ...
  end;

{ TCallhandlerGroup }
procedure TCallhandlerGroup.AddCallhandler(Sender:TObject);
  var tmpCallhandler:TCallhandler;
begin
    tmpCallhandler:=TCallhandler.create(self);
    ...
    tmpCallhandler.OnCallhandlerStateChange := OnCallhandlerChange;
    ...
end;

procedure TCallhandlerGroup.OnCallhandlerChange(aCallhandler:TCallhandler; aCallstate:APICallstateEnum);
begin
   ...
   UpdateCounters;
   ...
end;

procedure TCallhandlerGroup.UpdateCounters(Sender:TObject);
  var i: integer;
begin
  FSumIdle:=0;
  FSumConnecting:=0;
  FSumConnected:=0;

  for i:= Items.count -1 downto 0 do
  begin
     with TCallhandler(Items[i]) do
     begin
       case Callstate of
         csIdle: inc(FSumIdle);
         csConnecting: inc(FSumConnecting);
         csConnected: inc(FSumConnected);
       end;
     end;
  end;
end;

procedure TCallhandlerGroup.GetUsageCounters(var callhandlerCount,idle,connecting,connected:intger);
begin
  callhandlerCount := count; // Items.count der ObjectList
  idle := FSumIdle
  connecting := FSumConnecting;
  connected := FSumConnected;
end;

Selbst auf die Gefahr hin, das ich mich oben bein skizzieren der Klassen vertippt habe.
Das System läuft mit 2 Callhandlern stabil.

Leider habe ich zuhause keine Möglichkeit das System mit 120 Kanälen (Callhandlern) zu testen.
Wenn die Software beim Kunden installiert wird sollte Sie natürlich nicht gleich abrauchen

Alle Instanzen laufen im Haup-Thread der Applikation.

Daher hier ein paar Fragen zur Sicherheit:

Kann der System Probleme bekommen :
a) wenn "UpdateCounters" den Callstate des Callhandler zählt während des Eventhandler "TCallhandler.OnAPICallstateChange" (asynchron ausgelöst vom COM Object) den Status des Callhandlers setzt? (also ggf. Exception weil Leseversuch während Schreibzugriff ?)

b) das die Ergebnisse "GetUsageCounters" nicht immer stimmen, weil ein "TCallhandler.OnAPICallstateChange" den Callstate eines Callhandlers ändert, währens UpdateCounters die Nutzung ermittelt ?

c) wenn ich hypothetisch die monentane Auslastung für Statistikzwecke in einen TDataset schreiben möchte ...
Ist es z.B. möglich, dass GetUsageCounters 2x zur selben Zeit ausgeführt wird, weil 2 Callhandler gleichzeitig ihre Statusänderung über den Eventhandler "FCallhandlerStateChangeEvent" publizieren wollen? Falls ja würde ich Probleme erwarten, wenn ein Callhandler grade den TDataset in den Edit-Modus setzen will, während ein anderer noch in Bearbeitung hat.


Wenn es keine Möglichkeit gibt, das ganze zu sauber zu "entkoppeln", dann richte ich lieber Pro Callhandler einen Thread ein, und synchronisiere die Sachen über TCriticalSection bzw. Synchronize ...



Schöne Grüße,
Jens
  Mit Zitat antworten Zitat
jensw_2000
(Gast)

n/a Beiträge
 
#2

Re: Eventhandler für asynchron arbeitendende Objekte

  Alt 10. Jan 2008, 17:13
Da ich nur einen Thread - den Haupt-Thread der Applikation - habe und die Eventhandler durch die API des Herstellers synchronisiert aufgerufen werden, gehe ich davon aus, das immer ein Event zur Zeit ausgelöst wird, dass anschließend die Ereignisbehandlingsroutine aufgerufen und incl. aller darin aufgerufenen Subroutinen abgearbeitet wird. Anschleißend feuert das nächste, ggf. anstehende, Event der API. Somit sind also keine Parallelitätsprobleme zu erwarten.

Leider bin ich mir nicht sicher, ob es wirklich so einfach ist ....
Daher stelle ich meine Frage noch einmal nach oben.


Schöne Grüße,
Jens
  Mit Zitat antworten Zitat
Der_Unwissende

Registriert seit: 13. Dez 2003
Ort: Berlin
1.756 Beiträge
 
#3

Re: Eventhandler für asynchron arbeitendende Objekte

  Alt 10. Jan 2008, 19:02
Hi,
all die Probleme die Du nennst sind im Prinzip immer möglich. Um sie zu vermeiden muss eben synchronisiert bzw. gesperrt werden. Das Lesen bzw. Schreiben eines primitiven Typen wird i.d.R. als atomar angesehen (stellt sich immer die Frage was primitiv hier bedeutet), der ganze Rest kann immer Überschneidungen mit sich bringen.

Wenn Du auf eine fremde Komponente zugreifst, so ist es einfach wichtig zu wissen, ob diese Threadsafe ist oder nicht. Ist dies nicht der Fall, musst Du Dich selbst um die Synchronisation kümmern, mit allen Freiheiten und Gefahren. Dabei basiert gute Synchronisation immer auf dem Prinzip so spät wie möglich, so lang wie nötig (also bei Sperrsynchronisation). Man sperrt den Zugriff auf Ressourcen also immer erst, wenn es wirklich nötig wird und gibt die Sperren möglichst schnell wieder frei, hält sie aber auch wirklich bis sicher ist, dass es nicht zu einer Verklemmung kommen kann.

Zitat von jensw_2000:
Da ich nur einen Thread - den Haupt-Thread der Applikation - habe und die Eventhandler durch die API des Herstellers synchronisiert aufgerufen werden, gehe ich davon aus, das immer ein Event zur Zeit ausgelöst wird, dass anschließend die Ereignisbehandlingsroutine aufgerufen und incl. aller darin aufgerufenen Subroutinen abgearbeitet wird. Anschleißend feuert das nächste, ggf. anstehende, Event der API. Somit sind also keine Parallelitätsprobleme zu erwarten.
Genaus das stimmt so nicht bzw. muss so nicht stimmen. Das Event-Handling kann sehr gut von der Benachrichtigung über das Eintreffen eines Ereignisses entkoppelt werden. Hier kann die API z.B. auf einen Dispatcher zugreifen, der das Event-Handling auf Worker-Threads verteilt. Sind diese threadsafe, so wird dabei keineswegs verlangt, dass die nicht parallel arbeiten, es muss eben nur sichergestellt werden, dass das Result der seriellen Abarbeitung entspricht. Wie das erreicht wird ist egal (da gibt es Möglichkeiten wie Sperrsynchronisation, Zeitstempel-Verfahren oder auch optimistische Verfahren die einfach keinen Konflikt annehmen und ggf. eben eine Aktion rückgängig machen). Das beste Verfahren kann man nicht benennen, kommt immer darauf an, was für Zugriffe erfolgen.

Jedenfalls musst Du hier wirklich schauen, was genau die Komponente für Dich bereits übernimmt. Alles was Du selbst implementierst ist erstmal nicht threadsafe (außer die Komponente sichert das irgendwo zu oder Du sorgst selbst dafür). Davon auszugehen, dass die Komponente immer ein Event komplett abarbeitet bevor das nächste bearbeitet wird solltest Du auf keinen Fall. Selbst wenn das auf verschiedenen Systemen 999.999 mal der Fall ist, beim nächsten mal muss das nicht der Fall sein. Hier kann man sich nie auf Beobachtung verlassen, man muss es sicherstellen (z.B. durch eine Zusicherung der Doku oder eben durch ein eigenes Verfahren).

Wie gesagt, möglich/denkbar sind viele Probleme, kommt drauf an was die Komponente schon leistet (oder eben nicht). Selbst synchronisieren solltest Du nur wenn Du musst (kostet Perfomance und ist fehleranfällig). Ist die schon synchronisiert, solltest Du auf keinen Fall noch zusätzliche Sperren einführen (der Overhead ist schnell größer als jeglicher Gewinn durch die parallele Abarbeitung).

Gruß Der Unwissende
  Mit Zitat antworten Zitat
jensw_2000
(Gast)

n/a Beiträge
 
#4

Re: Eventhandler für asynchron arbeitendende Objekte

  Alt 11. Jan 2008, 22:19
Ich habe nun eine Antwort von dem API Lieferanten.
Alle Eventhandler und Callbacks werden dort synchronisiert aufgerufen. Die API ist also threadsicher.

Bleibt nur noch die Frage wie Delphi arbeitet ...

IMHO können alle Events nur rein seriell abgearbeitet werden, da alle Events im HauptThread meiner Applikation behandelt werden.

Das Ablaufschema sollte also immer so aussehen:

Delphi-Quellcode:
> OnIrgendEinEvent Start
>> EineSubProcedure 1
>> EineSubProcedure 2
>> EineSubProcedure 3
> OnIrgendEinEvent Ende

> OnIrgendEinEvent Start
>> EineSubProcedure 1
>> EineSubProcedure 2
>> EineSubProcedure 3
> OnIrgendEinEvent Ende

> OnIrgendEinEvent Start
>> EineSubProcedure 1
>> EineSubProcedure 2
>> EineSubProcedure 3
> OnIrgendEinEvent Ende
und nie so:

Delphi-Quellcode:
> OnIrgendEinEvent Start
>> EineSubProcedure 1
>> EineSubProcedure 2
> OnIrgendEinEvent Start
>> EineSubProcedure 3
>> EineSubProcedure 1
>> EineSubProcedure 2
> OnIrgendEinEvent Ende
>> EineSubProcedure 2
>> EineSubProcedure 3
> OnIrgendEinEvent Ende
Fakt ist, dass ich die Eventbehandlungen so schlank wie möglich halten muss, damit ich mich auf den Status der Callhandler verlassen kann. Für länger laufende Prozeduren werde ich besser an Alzaimars Workerthread bemühen ...


Danke.

Schöne Grüße,
Jens
  Mit Zitat antworten Zitat
Der_Unwissende

Registriert seit: 13. Dez 2003
Ort: Berlin
1.756 Beiträge
 
#5

Re: Eventhandler für asynchron arbeitendende Objekte

  Alt 12. Jan 2008, 09:21
Zitat von jensw_2000:
IMHO können alle Events nur rein seriell abgearbeitet werden, da alle Events im HauptThread meiner Applikation behandelt werden.
Das stimmt so nicht. Du kannst das einfach testen, nimm zwei Buttons und ein Memo. Lass den Eventhandler des ersten Button einfach in einer Schleife irgendwas ins Memo schreiben. Am einfachsten nimmst Du eine while (true) - Endlosschleife, ganz wichtig: Die sollte auch ein Application.ProcessMessages enthalten. Beim zweiten Button kannst Du Dir dann eine andere Aktion (z.B. ein ShowMessage) ausdenken.
Aktivierst Du nun die endlosschleife, so solltest Du theoretisch immer noch auf den 2ten Button klicken können und das wiederum sollte seine Ereignisbehandlung auslösen.

An sich ist hier nicht die Frage, aus welchem Thread heraus die Ereignisbehandlung gestartet wird, wichtig ist in welchem Thread die Ereignisbehandlung abläuft. Sobald diese in einem eigenen Thread läuft (kann z.B. auch durch eine verwendete Komponente der Fall sein) wird auch eine nicht-sequentielle Ausführung möglich.
  Mit Zitat antworten Zitat
Benutzerbild von thkerkmann
thkerkmann

Registriert seit: 7. Jan 2006
Ort: Pulheim Brauweiler
464 Beiträge
 
Delphi 2010 Professional
 
#6

Re: Eventhandler für asynchron arbeitendende Objekte

  Alt 12. Jan 2008, 10:58
Hi,

natürlich stimmt das so.
Die Unterbrechung der seriellen Abarbeitung kommt aussschliesslich durch das Application.ProcessMessages zustande.
Wenn er das nicht in seine serielle Abarbeitung einbaut, hat er genau das was er will.

Gruss
Thomas Kerkmann
Ich hab noch einen Koffer in Borland.
http://thomaskerkmann.wordpress.com/
  Mit Zitat antworten Zitat
Der_Unwissende

Registriert seit: 13. Dez 2003
Ort: Berlin
1.756 Beiträge
 
#7

Re: Eventhandler für asynchron arbeitendende Objekte

  Alt 12. Jan 2008, 15:16
Zitat von thkerkmann:
Die Unterbrechung der seriellen Abarbeitung kommt aussschliesslich durch das Application.ProcessMessages zustande.
Wenn er das nicht in seine serielle Abarbeitung einbaut, hat er genau das was er will.
Ich bin mir nicht sicher, ob der Aufruf von Application.ProcessMessages generell nötig ist. Was passiert denn, wenn man den Prozess z.B. mit MsgWaitForMultipleObjects kurz schlafen legt? Selbst wenn nur Application.ProcessMessages zum nicht-sequentiellen Abarbeiten führt, es dürfte schwer (unmöglich) sein, dass man einen Aufruf komplett ausschließt. So kann man zwar im eigenen Code noch auf einen entsprechenden Aufruf ggf. verzichten, wird aber im Eventhandler eine Fremdkomponente verwendet fällt das schwer. Hier sollte man (imho) also nicht davon ausgehen, dass es immer zu einer kompletten Abarbeitung kommt, bevor das nächste Ereignis signalisiert wird. Wenn dieses Verhalten garantiert werden muss, dann denke ich muss man an der Stelle auch explizit dafür sorgen.
  Mit Zitat antworten Zitat
jensw_2000
(Gast)

n/a Beiträge
 
#8

Re: Eventhandler für asynchron arbeitendende Objekte

  Alt 12. Jan 2008, 19:16
Ich habe nun alle Prozeduren, in denen eine serielle Bearbeitung nötig ist, durch eine CriticalSection geschützt.
Wenn die Eventhandler von Haus aus seriell aufgerufen werden, spielt es zeitlich fast keine Rolle, ob die Prozeduren durch die CS laufen. Wenn Events zufällig absolut zeitgleich ausgelöst werden, bringt die CS hoffentlich den notwendigen Schutz.
Laut dem Debug-Protokoll meiner Applikation wird die CS auf meinem Rechner innerhalb 1-2 Millisekunden durchlaufen (bei 2 Callhandlern). Beim Kunden (mit 120 Callhandlern) sollte der kritische Abschnitt also nach max. 120ms durchlaufen sein. Das ist für mich OK.

Die CS sorgt ja dafür, dass der geschützte Code-Abschnitt nur von einem Thread zur Zeit durchlaufen werden kann. Alle Eventhamdler werden im HauptThread meiner Applikation ausgeführt. Daher stellt sich also noch die Frage, ob die CS auch wirksam wird.



Schöne Grüße,
Jens
  Mit Zitat antworten Zitat
Antwort Antwort


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 19:01 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