Forum: PC-Programmierung Delphi - auf Event in einem Thread reagieren


von Christian S. (wirres_zeug)


Lesenswert?

Hallo,

ich arbeite hier an einer Software, die mittels DAQ-Karten von Advantech 
(PCIE-1744) Messungen durchführen soll. Dabei kommt der DAQNavi-Treiber 
zum Einsatz. System ist ein 32Bit Windows XP und Delphi 2010.
Die OLE-Komponente für den Analog Input löst immer dann ein Event aus, 
wenn Daten im Puffer des Treibers zum abholen bereit stehen. Dem Event 
eine Behandlungsroutine zuzuweisen ist kein Problem. Jedoch belastet das 
Event den Main-Thread der Applikation, der auch so schon recht viel zu 
tun hat. Die Behandlungsroutine ist schon so kurz wie möglich gehalten 
und kopiert die Daten nur in einen anderen Puffer zur Weitervarbeitung 
um.

Sinnvoll wäre es jetzt, auch da das Zielsystem über 8 Kerne verfügt, 
wenn ein zusätzlicher Thread auf das Event reagieren würde und das 
kopieren erledigt. Im Moment ist es so, dass das Event normal ausgelöst 
wird und nur ein Flag gesetzt wird. Der Thread zum kopieren fragt dieses 
Flag dann regelmäßg ab und startet bei Bedarf den Kopiervorgang.
Ich hätte jetzt aber gern, dass der Thread dirket auf das (und andere) 
Event des Treibers reagieren würde, so dass der Main-Thread komplett 
außen vor bleibt. Ich weiß nur leider nicht, wie ich das anstelle. Meine 
Suchen zu dem Thema waren bisher erfolglos. Vielleicht fehlt mir auch 
nur der richtige Begriff.

Hat jemand von euch shconmal so etwas gemacht?

Vielen Dank,

Christian

von Oliver R. (superberti)


Lesenswert?

Hi!

Also mein Delphi-Wissen ist schon etwas her, aber ich bin der Meinung, 
dass immer nur der GUI-Thread selbst in der Lage ist Events 
abzuarbeiten. Events sind keine direkten Closures und funktionieren 
intern über WINDOWS-Nachrichten.
So weit mir bekannt war es unter Delphi/BCB schon immer problematisch 
bis unmöglich, Forms in einem neuen Thread zu erstellen. Theoretisch 
könnte man dann diesem neuen Form die Abarbeitung der Events überlassen 
(indem man den Event zur Laufzeit zuweist, also z.B. 
Komponente->MeinEvent = ThreadForm->MeineEventFunktion).
Darauf würde ich mich aber nicht einlassen und lieber so arbeiten, wie 
Du es jetzt schon machst. Anstatt ein Flag zu setzen (was der Thread 
dann pollt) solltest Du lieber mit einem oder mehreren TEvents (die 
haben lustigerweise NICHTS mit den oben genannten Events gemein...) 
arbeiten, auf die der Thread dann wartet. Bei einem Event mit 
TEvent->WaitFor() bei mehreren mit WaitForMultipleObjects und Konsorten.

Gruß,
Oliver

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Oliver R. schrieb:
> aber ich bin der Meinung,
> dass immer nur der GUI-Thread selbst in der Lage ist Events
> abzuarbeiten. Events sind keine direkten Closures und funktionieren
> intern über WINDOWS-Nachrichten.

Wenn "Event" ein Event im Sinne der Win32-API meint, dann irrst Du Dich 
glücklicherweise.

Ein Thread wartet auf ein Event mit den Funktionen WaitForSingleObject 
bzw. WaitForMultipleObjects.

Erzeugt wird ein Event mit CreateEvent, signalisiert wird es mit 
SetEvent bzw. ResetEvent (letzteres ist nur bei nicht-automatisch 
rücksetzenden Events erforderlich).

Das funktioniert mit allen Threads und hat nichts mit 
Windows-Nachrichten zu tun.

von Oliver R. (superberti)


Lesenswert?

Hi,

Rufus Τ. Firefly schrieb:
>
> Wenn "Event" ein Event im Sinne der Win32-API meint, dann irrst Du Dich
> glücklicherweise.
>
Nein, das meinte ich nicht. Borland (bzw. deren unaussprechliche 
Nachfolger) bezeichnen einen VCL-Event als Behandlungsereignis einer 
Komponente (meistens Oberfläche, z.B. ein Button). Das wird dann vom 
Dispatcher des Hauptthreads abgearbeitet.
Nun gibt es da noch das Objekt "TEvent", welches die von Dir genannten 
WIN32-API-Events kapselt. Leider vom Sprachgebrauch sehr ähnlich, aber 
konzeptionell absolut nicht miteinander verwandt.

Gruß,
Oliver

von und nun (Gast)


Lesenswert?

Ich mach ueblicherweise auch solche Dinge, hab's aber nicht grad 
zugriffsbereit. In der Zwischenzeit empfehle ich das Embarcadero Forum. 
Die Experten sind dort.

von Christian S. (wirres_zeug)


Lesenswert?

Oliver R. schrieb:
> Nein, das meinte ich nicht. Borland (bzw. deren unaussprechliche
> Nachfolger) bezeichnen einen VCL-Event als Behandlungsereignis einer
> Komponente (meistens Oberfläche, z.B. ein Button). Das wird dann vom
> Dispatcher des Hauptthreads abgearbeitet.

Ja, um solch ein Event handelt es sich. Zumindest bin ich mir da sicher, 
es sieht so aus, riecht so und schmeckt so wie auch z.b. ein 
OnClik-Event eines TButtons.
Ich zitiere mal aus der TLB zum Treiber:
1
TBufferedAiCtrl = class(TOleControl)
2
  private
3
    FOnDeviceRemoved: TNotifyEvent;
4
    FOnDeviceReconnected: TNotifyEvent;
5
    FOnPropertyChanged: TNotifyEvent;
6
    FOnDataReady: TBufferedAiCtrlDataReady;
7
    FOnOverrun: TBufferedAiCtrlOverrun;
8
    FOnCacheOverflow: TBufferedAiCtrlCacheOverflow;
9
    FOnStopped: TBufferedAiCtrlStopped;
10
    FIntf: IBufferedAiCtrl;
11
    [...]
12
    property OnDeviceRemoved: TNotifyEvent read FOnDeviceRemoved write FOnDeviceRemoved;
13
    property OnDeviceReconnected: TNotifyEvent read FOnDeviceReconnected write FOnDeviceReconnected;
14
    property OnPropertyChanged: TNotifyEvent read FOnPropertyChanged write FOnPropertyChanged;
15
    property OnDataReady: TBufferedAiCtrlDataReady read FOnDataReady write FOnDataReady;
16
    property OnOverrun: TBufferedAiCtrlOverrun read FOnOverrun write FOnOverrun;
17
    property OnCacheOverflow: TBufferedAiCtrlCacheOverflow read FOnCacheOverflow write FOnCacheOverflow;
18
    property OnStopped: TBufferedAiCtrlStopped read FOnStopped write FOnStopped;
19
  end;

Konkret geht es um die Events OnDataReady, OnOverrun und 
OnCacheOverflow. Für WaitForMultipleObjects() benötigt man ja den Handle 
des Events. Nur, wie bekomm ich den raus?

von Oliver R. (superberti)


Lesenswert?

Hi,

also nochmal: Die VCL-Events (die Events Deiner Komponente) haben NICHTS 
mit den WIN32-API-Events zu tun. Deshalb gibt es da auch kein Handle. 
Aber Du kannst in Deinem OnDataReady-VCL-Event ein 
WIN32-API-Event-Objekt, welches durch das Delphi-Objekt "TEvent" 
gekapselt wird, signalisieren, dass neue Daten da sind.
Dafür legst Du im Konstruktor Deines Forms ein oder mehrere 
TEvent-Objekte an und wartest in Deinem Thread auf diese Objekte, ob was 
passiert.
Damit vermeidet man das Polling des Threads auf irgendwelche Flags.
Desweiteren musst Du bei deinem Form aufpassen, dass es NIE durch 
längere Aktionen blockiert wird. Dann blockiert nämlich auch der 
Dispatcher und es werden keine VCL-Events abgearbeitet.
Evtl. gibt es bei Deiner OLE-Komponente aber auch noch die Möglichkeit, 
synchron auf neue Daten zu warten (also. z.B. 
Komponente->WaitForNewData()). Das könnte man dann direkt im Thread 
machen und man würde auf Events ganz verzichten.

Gruß,
Oliver

von Athena (Gast)


Lesenswert?

Oliver R. schrieb:
> Damit vermeidet man das Polling des Threads auf irgendwelche Flags.
> Desweiteren musst Du bei deinem Form aufpassen, dass es NIE durch
> längere Aktionen blockiert wird.

Das ist Standard bei OOP und Ereignissteurerung und keine Besonderheit 
von Delphi.

Hier Hintergrundinformation zum Thema:
http://www.delphi-treff.de/tutorials/vcl/komponenten-entwicklen/ereignisse-events/

von CIMS_ES (Gast)


Lesenswert?

Hallo,

leider kann ich zu Delphi nichts sagen. Wir nutzen C++, mit der DAQ NAVI 
Umgebung. Die Daten werden in einer DataHandlerKlasse, welche vom 
Advantech DaqNavi BfdAiEventListener erbt, verarbeitet. Die 
Kartensoftware ruft darin die Memberfunktion  BfdAiEvent(void * sender, 
BfdAiEventArgs * args) auf, wenn Daten verfügbar sind. Die Daten werden 
dann mittels der Funktion GetData(sampleCount, m_dataPtr); in einen 
lokalen Speicher geschrieben und können dann von einem anderen Thread 
verarbeitet werden. Den Speicher für die Daten muss man selbst zur 
Verfügung stellen.

ABER

wir haben eine solche Karte Anfang des Jahres in einem System 
eingesetzt. Es hat sich nun herausgestellt, dass die Karte zumindest in 
unserem System ihre Sample-Rate absolut nicht einhält. Die von Advantech 
mitgelieferte DAQNavi Applikation zeigt im Testmode bei periodischen 
Signalen bei jedem Start immer mal wieder mit falscher Rate abgetastete 
Daten im Testdisplay an. Wir verwenden 10 MS/s und die Daten werden 
gelegentlich mit 20 MS und auch langsamer abgetastet. Mich würde mal 
interessieren, ob das bei Euch auch so ist.

Hinzu kommt, dass die PCIE-1744 Karte eine PCIe-PCI Bridge enthält, die 
nach Aussage von Adv. eine zusätzliche Latency in den Interrupt betrieb 
hineinbringt und so bei höheren Raten kritisch wird. Wir haben jetzt die 
PCI Version der Karte also die PCI 1714 geordert in der Hoffnung die 
Probleme zu vermeiden.

von Christian S. (wirres_zeug)


Lesenswert?

Hallo CIMS_ES,

dass die Samplerate manchmal nicht korrekt gesetzt ist, ist mir mit den 
Demoapplikationen auch schon einmal aufgefallen (unter Delphi). Bzw. ist 
mir dort aufgefallen, dass eben bei angelegten Rechtecksignal zwischen 
zwei Starts ab und an unterschiedlich viele Perioden zu sehen waren. Hab 
es aber nicht untersucht und auf die Darstellung geschoben.
Bei unserer Software bzw. Anwendung benötigen wir nur einen kleinen 
Aussschnitt mit hoher Auflösung, weshalb nach die restlichen Daten 
verworfen werden. Nach manchen Starts kann die Software zwar triggern, 
aber in dem aufgezeichneten Ausschnitt ist nur Rauschen, aber nicht das 
zu untersuchende Signal. Das würde dazu passen, dass die Samplerate 
nicht richtig gesetzt ist.

Was aber noch viel schlimmer ist, vor allem für unsere Anwendung: unter 
Delphi mit der ActiveX Typbibliothek sind die Kanäle nicht alle 
synchron. Also Kanal 1 zu 2 und 3 zu 4 bleiben synchron, aber die beiden 
Paare laufen auseinander. Das tritt bei unserer Software als auch bei 
den Examples auf. Unter C# mit der .net-Typbibliothek passiert das 
nicht.

Ebenfalls merkwürdig: nach mehrmaligen Starten der Applikation ist 
irgendwann die Karte nicht mehr ansprechbar und Windows muss neu 
gestartet werden.

Wir versuchen es jetzt erst einmal noch mit Workarounds.

von Jim M. (turboj)


Lesenswert?

Christian S. schrieb:
> Konkret geht es um die Events OnDataReady, OnOverrun und
> OnCacheOverflow. Für WaitForMultipleObjects() benötigt man ja den Handle
> des Events. Nur, wie bekomm ich den raus?

Sieht für mich eher nach Window Message Handlern aus - TOleControl erbt 
aus TWinControl. Die werden in dem Thread behandelt, der das Window 
erstellt hat.

Du musst also dein TBufferedAiCtrl in einem Thread erstellen, der auch 
eine Message Loop enthält. Es kann sein, dass die Message Queue vorher 
mit einem Call zu PeekMessage() "scharf" gestellt werden muss.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.