![]() |
[Thread] Zeichnen hört teilweise auf
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo Leute,
ich habe ein merkwürdiges Problem dessen Ursache ich nicht finden kann und welches auch nicht reproduziebar ist, sondern scheinbar nur zufällig auftritt. :? Gegeben ist folgende Situation: Ich habe ein Programm, welches über den COM-Port mit einer Platine kommuniziert, auf welcher sich Leuchtdioden befinden, die Buchstaben anzeigen können. Das funktioniert auch. Über einen bestimmten Befehl kann man den Helligkeitsstatus der Platine auslesen, je heller es ist, desto heller leuchten die Leuchtdioden, damit man den angezeigten Text trotzdem lesen kann. Auch das funktioniert. Das Programm fragt periodisch den Helligkeitsstatus des Geräts ab und wertet diesen aus. Die Abfragen werden in einem Thread gemacht, da die Antwort des Gerätes manchmal nicht sofort kommt. Die Werte liegen zwischen null und 65535, wobei null 0% und 65535 100% entspricht. In Abb. eins (Siehe Anhang) wird der aktuelle Wert als rote Linie dargestellt, die letzten fünf als Graue. Die Werte stehen oben links, oben rechts steht die Anzahl der Abfragen, sowie die Uhrzeit der letzten Abfrage. In Abb. zwei wird der Verlauf dargestellt, jede rote Linie steht für eine Abfrage. Die Werte die in den Labels sind die Gleichen wie in Abb. eins, und nur zu Testzwecken nochmals aufgeführt, denn: Das Problem ist, dass das Programm manchmal aufhört, Abb. eins zu aktualisieren. Dies tritt nur ab und zu auf, mal öfter mal seltener, heute bisher zweimal, gestern nur einmal und vorgestern relativ häufig. Zunächst dachte ich, dass der Thread Probleme macht, dies ist aber nicht so, denn die Labels und Abb. zwei werden nach wie vor richtig aktualisiert. Hier erstmal meine Threadklasse mit Erläuterung:
Delphi-Quellcode:
Die Execute Methode sieht (gekürzt) wie folgt aus:
TBrighnessValuesThread = class (TThread)
private FLDP1Controller: TLDP1Controller; //Die Klasse, die sich um die Kommunikation mit dem Gerät kümmert //Es wird zunächst alles auf ein TBitmap gemalt, FBuffer ist für Abb. eins und FBufferCurve ist für Abb. zwei FBuffer: TBitmap; FBufferCurve: TBitmap; //Die Images werden im Konstruktor übergeben FImage: TImage; //Abb. eins FCurveImage: TImage; Abb. zwei //[...] (Ein paar für dieses Problem unwichtige Felder) //Die Labels, die zusätzlich die Werte anzeigen FLabels: TLabelArray; //Diese Prozedur sorgt dafür, dass die bemalten Buffer auf die Images übertragen werden (Wird mit synchronize aufgerufen) procedure BufferOnImage; protected //Dürfte klar sein ;-) procedure Execute; override; public //[...] Ein paar Properties //Der Konstruktor, hier werden die wichtigen Parameter wie constructor Create(ALDP1Controller: TLDP1Controller; AImage,ACurveImage: TImage; const AUnitNumber: byte; ASteps: integer); reintroduce; destructor Destroy; override; end;
Delphi-Quellcode:
Das Zeichnen von Abb. zwei hab ich hier jetzt mal rausgelassen, da dort keine Probleme auftreten.
procedure TBrighnessValuesThread.Execute;
var Values: TBrightnessStateStruct; //Dort werden die ausgelesenen Werte reingeschrieben hlp: double; Last5Values: Array [0..4] of TBrightnessStateStruct; //Hier i: Integer; Counter: integer; begin inherited; //[...] Ein bisschen Initialisierungskram //Hauptschleife while not Terminated do if FLDP1Controller.Connection.Connected then begin //[...] Zunächst mal neue Daten holen und in Values schreiben //Pseudocode: Values := GetDaten; inc(Counter); //Altes Bild löschen und Rahmen zeichnen und Zahl und Zeit FBuffer.Canvas.Rectangle(0,0,FBuffer.Width,FBuffer.Height); FBuffer.Canvas.TextOut(FBuffer.Width-30,10,inttostr(Counter)); FBuffer.Canvas.TextOut(FBuffer.Width-50,25,FormatDateTime('hh:nn:ss',now)); //Die letzten 5 Werte zeichnen, in grau FBuffer.Canvas.Font.Color := $CCCCCC; FBuffer.Canvas.Pen.Color := $DDDDDD; for i := 4 downto 0 do begin //Prozentwert ausrechnen hlp := 100/65535*Last5Values[i].sensor_val; //Und aufs Bild schreiben FBuffer.Canvas.TextOut(10,25+i*15,FloatToStrF(hlp,ffFixed,10,2)+' % ('+inttostr(Last5Values[i].sensor_val) +')'); //Und auch in die Labels! FLabels[i+1].Caption := FloatToStrF(hlp,ffFixed,10,2)+' % ('+inttostr(Last5Values[i].sensor_val)+') | '+inttostr(Last5Values[i].lststrom)+'/'+inttostr(Last5Values[i].solistrom); //Linie Zeichnen FBuffer.Canvas.MoveTo(1,FBuffer.Height-Trunc(hlp/100*FBuffer.Height)); FBuffer.Canvas.LineTo(FBuffer.Width-1,FBuffer.Canvas.PenPos.Y); end; //Und jetzt noch den aktuellen Wert berechnen und ausgeben FBuffer.Canvas.Pen.Color := clred; FBuffer.Canvas.Font.Color := clred; hlp := 100/65535*Values.sensor_val; FBuffer.Canvas.TextOut(10,10,FloatToStrF(hlp,ffFixed,10,2)+' % ('+inttostr(Values.sensor_val)+')'); FLabels[0].Caption := FloatToStrF(hlp,ffFixed,10,2)+' % ('+inttostr(Values.sensor_val)+') | '+inttostr(Values.lststrom)+'/'+inttostr(Values.solistrom); FBuffer.Canvas.MoveTo(1,FBuffer.Height-Trunc(hlp/100*FBuffer.Height)); //Wert Zeichnen FBuffer.Canvas.Pen.Color := clred; FBuffer.Canvas.LineTo(FBuffer.Width-1,FBuffer.Canvas.PenPos.Y); FBuffer.Canvas.Pen.Color := 0; //Bilder auf Form übertragen Synchronize(BufferOnImage); //Werte verschieben for i := 3 downto 0 do begin Last5Values[i+1] := Last5Values[i]; end; Last5Values[0] := Values; end; end; Die Sache ist, dass er die ganzen Zeichenroutinen durchgeht, auch wenn das Bild nicht aktualisisert wird. Die Labels bekommen exakt die gleichen Werte wie das Bild, aber die Lables werden weiterhin aktualisiert, genau wie Abb. zwei. Es kann also eigentlich nur daran liegen, dass er das Bild nicht mehr auf das Image überträgt, aber WARUM? Es tritt kein Fehler auf, und die Execute Methode läuft genauso wie immer durch. Wie kommt es also, dass NUR Abb. eins nicht weiter gezeichnet wird? Vor allem da dies absolut unregelmäßig auftritt... Mein Kopf raucht schon :gruebel: Ich hoffe ich habe alle wichtigen Informationen gegeben und dass mir jemand einen Tipp geben kann... danke schonmal, Ritsch |
Re: [Thread] Zeichnen hört teilweise auf
1. keine Oberflächen VCL Objekte innerhalb des Threads erzeugen
2. Aktionen auf Oberflächen VCL Elementen immer synchronisieren aus dem Thread heraus! Die VCL ist nicht thread-safe! |
Re: [Thread] Zeichnen hört teilweise auf
Die VCL Objekte werden auch nicht im Thread erzeugt, sondern nur als Parameter dem Konstruktor übergeben. Die Bilder werden im Synchronize übertragen (siehe Zeile 55 im Quelltext).
Trotzdem danke für deine Antwort! EDIT: Nicht unbedingt Zeile 55 :mrgreen: halt da wo Synchronize(BufferOnImage); steht |
Re: [Thread] Zeichnen hört teilweise auf
Mir ist aufgefallen, daß Du Deine procedure BufferOnImage mit synchronize aufrufst, obwohl sie innerhalb des Threads steht. Synchronize hält meiner Meinung nach den Thread an bis externe prozeduren abgearbeitet sind. Dieses Thread-interne Anhalten erscheint mir nicht logisch. Ich habe aber keine Ahnung, welche Folgen das hat.
Außerdem: hast Du irgendwo im Programm Wairforsingleobject mit einerm timeout verwendet? Dann könnte passieren, das Teile des Threads nicht ausgeführt, z.B. weil die Antwortzeit des Geräts besonders lang war. Es kann vielleicht auch sinnvoll sein, die Antwortzeiten zu messen und irgendwo zu puffern um einen möglichen Zusammenhang damit zu untersuchen. Der Hinweis von Muetze1 ist auch richtig: das beschreiben des bitmaps möglichst außerhalb Deines Threads umsetzen und diese Prozedur dann mit synchronize aufrufen. Grüße, Messie |
Re: [Thread] Zeichnen hört teilweise auf
Zitat:
Zitat:
Zitat:
Zitat:
|
Re: [Thread] Zeichnen hört teilweise auf
Vielleicht solltest du dir
![]() |
Re: [Thread] Zeichnen hört teilweise auf
Zitat:
|
Re: [Thread] Zeichnen hört teilweise auf
Zitat:
Zitat:
Delphi-Quellcode:
procedure TForm1.PushTheButton;
begin Button1.Click; end; procedure TMyThread.Execute; begin ... Synchronize(Form1.PushTheButton); ... end; Grüße, Messie |
Re: [Thread] Zeichnen hört teilweise auf
Zitat:
Welche Methode dort angegeben wird, ist völlig egal. Es kann genauso gut eine Methode des Threads sein. Dies sollte sogar bevorzugt werden, weil: a) Form1 eine globale Variable ist und nebenbei durch den VCL Thread bzw. alternativ "das Programm" sonstwie verändert werden (Zeit bis zum erreichen der Message vom Synchronize in der Message Queue). Dabei u.a. auch die Freigabe der Instanz etc und schon ist deine Methode weg - bzw greift auf nicht mehr gültigen Speicher zu, wenn sie Elemente nutzt. b) Die Thread-Methoden meistens nicht reentrant aufgerufen werden und u.a. auch leichter dagegen geschützt werden können (Deklaration im Private-Abschnitt des Threads) als die VCL Formular-Methoden bzw. Methoden im VCL Thread ("das Programm"). Diese müssen schliesslich public sein, damit sie für die Synchronize Methode angegeben werden können. Das bestehende Handling mit Synchronize muss vom Threadersteller nicht geändert werden, sondern ist so ok wie es ist. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:17 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz