Autor |
Beitrag |
NemesisoD
      
Beiträge: 43
|
Verfasst: So 16.03.08 20:58
Hallo zusammen,
ich schreibe zurzeit die Software für einen Flugsimulator Marke Eigenbau. Wir nutzen den MS Flightsimulator 2004 und lesen über eine Zusatz DLL die Variablen des FlightSims aus. Diese werden dann über einen virtuellen COM-Port an die selbstgebaute Elektronik gesendet.
Es funktioniert auch alles wunderbar, bis auf das ich bereits beim Auslesen einer Variable aus dem Flightsim eine merkliche Verzögerung habe. Ich nutze den Standard Timer mit einem Intervall von 1ms. Hier die Funktion:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28:
| procedure TMain.Timer1Timer(Sender: TObject); var frequenz : DWORD; dwResult : DWORD; KHZ : Byte; MHZ : Byte; begin if FSUIPC_Read($034E, 3, @frequenz, dwResult) then begin if FSUIPC_Process(dwResult) then begin frequenz := ((frequenz and not(($300000))) OR $10000); Label2.Caption := Format('%x',[frequenz]); KHZ := BCDVal(frequenz and $000000FF); MHZ := BCDVal(frequenz and $FFFFFF00); Label3.Caption := inttostr(MHZ) + '.' + inttostr(KHZ); if not((KHZ = VKHZ) or (MHZ = VMHZ)) then begin VMHZ := MHZ; VKHZ := KHZ; ApdComPort1.Output := chr(MHZ); ApdComPort1.Output := chr(KHZ); end; end else begin Label2.Caption := 'Processing: ' + ResultText[dwResult]; end; end; end; |
Ich muss die Variablen noch von BCD als Integer Kovertieren, und da es sich einmal um die Frequenz in MHZ und KHZ handelt muss ich das auchnoch trennen.
Meine Frage ist nun, ob es eine schnellere Möglichkeit gibt.
Das Konvrtieren könnte ich zurnot auch noch auf die Mikrocontroller packen.
Besten Dank schon mal
MfG
Michael
_________________ Wer nicht programmiert, der lebt nicht!
|
|
dummzeuch
      
Beiträge: 593
Erhaltene Danke: 5
Delphi 5 ent, Delphi 6 bis Delphi XE8 pro
|
Verfasst: Mo 17.03.08 22:16
NemesisoD hat folgendes geschrieben: | Ich nutze den Standard Timer mit einem Intervall von 1ms.
|
Ein Timer hat ein minimales Intervall von ca. 50 ms. Das ist nicht zu beeinflussen, da eine Einschraenkung von Windows.
Kann das evtl. der Grund sein? Ohne jetzt Deinen Code genauer angeschaut zu haben: Evtl. rechnest Du irgendwo mit 1 MHz obwohl es nur 200 Hz sind?
twm
|
|
NemesisoD 
      
Beiträge: 43
|
Verfasst: Mo 17.03.08 23:51
Hallo,
danke erstmal für deine Antwort. Die MHZ bezieht sich nicht auf eine Geschwindigkeit, das ist eine Frequenz welche ich aus dem Flugsimulator beziehe.
Aber ich habe mitlerweile eine neue Idee, könnte man das pollen nicht einfach in ein eigenes Thread auslagern, und immer wenn sich eine Variable ändert wird dies dem Haupt-Thread mittgeteilt und dieser sendet dann die Daten an die Elektronik.
Aber wie kann ich dem Haupt-Thread sagen das er was machen soll, ohne das das Neben-Thread aufhört zu arbeiten, weil die Sycronize Funktion bewirkt ja, das das Neben-Thread wartet bis das Haupt-Thread fertig ist. Kann man das vielleicht über ein Event machen? Aber wie bau ich mir ein solches???
Danke schonmal für eure Antworten.
Micha
_________________ Wer nicht programmiert, der lebt nicht!
|
|
dummzeuch
      
Beiträge: 593
Erhaltene Danke: 5
Delphi 5 ent, Delphi 6 bis Delphi XE8 pro
|
Verfasst: Di 18.03.08 14:43
NemesisoD hat folgendes geschrieben: |
Aber ich habe mitlerweile eine neue Idee, könnte man das pollen nicht einfach in ein eigenes Thread auslagern, und immer wenn sich eine Variable ändert wird dies dem Haupt-Thread mittgeteilt und dieser sendet dann die Daten an die Elektronik.
Aber wie kann ich dem Haupt-Thread sagen das er was machen soll, ohne das das Neben-Thread aufhört zu arbeiten, weil die Sycronize Funktion bewirkt ja, das das Neben-Thread wartet bis das Haupt-Thread fertig ist. Kann man das vielleicht über ein Event machen? Aber wie bau ich mir ein solches???
|
Wieso soll denn der Haupt-Thread an die Elektronik schicken? Das kann doch auch der Hintergrund-Thread machen. Oder Du startest extra dafuer einen neuen Thread und synchronisierst mittels eines TEvent-Objects.
Aber Vorsicht: Wenn Du noch nie ein multithreaded Programm geschrieben hast, kann das schnell in die Hose gehen. Z.B. Zugriffe auf die VCL aus Hintergrund-Threads heraus, sind verboten.
twm
|
|
Hidden
      
Beiträge: 2242
Erhaltene Danke: 55
Win10
VS Code, Delphi 2010 Prof.
|
Verfasst: Di 18.03.08 16:36
dummzeuch hat folgendes geschrieben: |
Zugriffe auf die VCL aus Hintergrund-Threads heraus, sind verboten.
|
Aber Massages kann man doch schicken. Das ist ein sehr einfacher Umweg, ansonsten TNotifyEvent.
Hörte sich jetzt so sehr nach man kann nichts beeinflussen an  .
mfG,
_________________ Centaur spears can block many spells, but no one tries to block if they see that the spell is a certain shade of green. For this purpose it is useful to know some green stunning hexes. (HPMoR)
|
|
FinnO
      
Beiträge: 1331
Erhaltene Danke: 123
Mac OSX, Arch
TypeScript (Webstorm), Kotlin, Clojure (IDEA), Golang (VSCode)
|
Verfasst: Di 18.03.08 18:25
Mal ne ganz dumme Frage: Muss man überhaupt nen Timer nehmen? Ich meine ne Endlosschleife ist doch schneller oder?
|
|
NemesisoD 
      
Beiträge: 43
|
Verfasst: Di 18.03.08 20:43
@FinnO
Ich bin jetzt auch weg vom Timer, ich will das ganze ja nun mit Threads und einer Endlosschleife lösen, mein Problem bei der sache ist nur, das ich nicht weiß wie ich aus einem Thread einem anderen Thread sage das er was machen soll, und das möglichst ohne eines der Threads anhalten zu müssen.
Würde mich über einen kuren Beispiel Code sehr freuen.
Leider müssten beide Threads auf die gleiche Daten zugreifen.
Gruß
Michael
_________________ Wer nicht programmiert, der lebt nicht!
|
|
Regan
      
Beiträge: 2157
Erhaltene Danke: 72
Java (Eclipse), Python (Sublimetext 3)
|
Verfasst: Di 18.03.08 20:49
Ich weiß nicht in wie fern dir das weiterhilft, aber ich würde dir mal das hier aus der DGL wiki zum Lesen anbieten.
|
|
dummzeuch
      
Beiträge: 593
Erhaltene Danke: 5
Delphi 5 ent, Delphi 6 bis Delphi XE8 pro
|
Verfasst: Di 18.03.08 20:56
Hidden hat folgendes geschrieben: | dummzeuch hat folgendes geschrieben: |
Zugriffe auf die VCL aus Hintergrund-Threads heraus, sind verboten.
|
Aber Messages kann man doch schicken. Das ist ein sehr einfacher Umweg, ansonsten TNotifyEvent.
|
TNotifyEvent ist auch nur ein Methodenaufruf, d.h. der Code darin wird dann auch im Hintergrundthread ausgefuehrt.
Senden von Messages an ein Window-Handle geht, kann allerdings schiefgehen, wenn das Handle noch nicht existiert, dann wird es naemlich vom Hintergrund-Thread erzeugt und man hat sehr komische Effekte.
Abgesehen davon, ist die Performance dabei eher schlecht. Damit musste ich mich in der letzten Zeit mehrfach rumschlagen. Jetzt laeuft das mit zwei zusaetzlichen Threads und das Programm hat nur noch knapp 45% CPU-Last statt vorher 95% und nebenbei reagiert es wieder besser auf Benutzereingaben. Aber das ist ein spezieller Fall mit ziemlich aufwendigen Berechnungen. In der Regel sollte es mit Messages funktionieren.
twm
|
|
dummzeuch
      
Beiträge: 593
Erhaltene Danke: 5
Delphi 5 ent, Delphi 6 bis Delphi XE8 pro
|
Verfasst: Di 18.03.08 21:20
NemesisoD hat folgendes geschrieben: | ich will das ganze ja nun mit Threads und einer Endlosschleife lösen, mein Problem bei der sache ist nur, das ich nicht weiß wie ich aus einem Thread einem anderen Thread sage das er was machen soll, und das möglichst ohne eines der Threads anhalten zu müssen.
|
Ungetestet:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63:
| uses Windows;
type TData = class end;
var gblData: TData = nil;
type TConsumerThread = class(TThread) protected procedure Execute; override; end;
type TProducerThread = class(TThread) protected procedure Execute; override; end;
procedure TProducerThread.Execute; var Data: TData; OldData: TData; begin while not Terminated do begin Data := TData.Create; GetData(Data); OldData := TData(InterlockedExchange(integer(gblData), integer(Data))); if Assigned(OldData) then OldData.Free; end; end;
procedure TConsumerThread.Execute; var Data: TData; begin while not Terminated do begin Data := TData(InterlockedExchange(integer(gblDdata), nil); if Assigned(Data) then begin HandleData(Data); Data.Free; end; end; end;
var Producer: TProducerThread; Consumer: TConsumerThread;
begin Consumer := TConsumerThread.Create(false); Producer := TProducerThread.Create(false); |
Der ProducerThread holt mittels des GetData-Aufrufs neue Daten. GetData kehrt erst zurueck, wenn neue Daten gelesen wurden und in den Data Parameter uebertragen wurden. Dann benutzt er InterlockedExchange, um diese neuen Daten in gblData zu speichern. Falls gblData ein noch nicht verarbeitetes Objekt enthielt, wird dieses freigegeben.
Der ConsumerThread holt sich mittels InterlockedExchange den Inhalt von gblData und setzt gleichzeitig diese Variable auf nil. Wenn sie Daten enthielt, werden sie durch Aufruf von HandleData verarbeitet und anschliessend freigegeben.
Das sollte so funktionieren, jedoch kann es Dir passieren, dass der ConsumerThread jede Menge Rechenzeit verbraet, da er gblData pollt. Dagegen hilft dann ein Sleep.
Diese Art der Datenuebergabe kommt ohne Locks aus, hat aber den Nachteil, dass, falls die Daten schneller einlaufen als sie verarbeitet werden, Daten verlorengehen (ProducerThread gibt alten Inhalt von gblData frei, wenn nicht nil).
Alternativ kannst Du auch eine TQueue benutzen, die Du aber mittels einer TCriticalSection vor gleichzeitigen Zugriff schuetzen musst.
(Ich bin mir sicher, dass diverse Leute bei diesem Code Haare in der Suppe finden werden. Ich will auch nicht behaupten, dass das die beste aller Loesungen ist, aber funktionieren sollte es.)
twm
|
|
|