AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

[Thread] Zeichnen hört teilweise auf

Ein Thema von Tormentor32 · begonnen am 8. Nov 2007 · letzter Beitrag vom 8. Nov 2007
Antwort Antwort
Benutzerbild von Tormentor32
Tormentor32

Registriert seit: 27. Okt 2005
Ort: Düsseldorf
369 Beiträge
 
Delphi XE5 Professional
 
#1

[Thread] Zeichnen hört teilweise auf

  Alt 8. Nov 2007, 10:26
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:
  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;
Die Execute Methode sieht (gekürzt) wie folgt aus:
Delphi-Quellcode:
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;
Das Zeichnen von Abb. zwei hab ich hier jetzt mal rausgelassen, da dort keine Probleme auftreten.

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

Ich hoffe ich habe alle wichtigen Informationen gegeben und dass mir jemand einen Tipp geben kann...

danke schonmal,

Ritsch
Miniaturansicht angehängter Grafiken
helligkeitsstatus_708.jpg  
Richard Mahr
  Mit Zitat antworten Zitat
Muetze1
(Gast)

n/a Beiträge
 
#2

Re: [Thread] Zeichnen hört teilweise auf

  Alt 8. Nov 2007, 11:11
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!
  Mit Zitat antworten Zitat
Benutzerbild von Tormentor32
Tormentor32

Registriert seit: 27. Okt 2005
Ort: Düsseldorf
369 Beiträge
 
Delphi XE5 Professional
 
#3

Re: [Thread] Zeichnen hört teilweise auf

  Alt 8. Nov 2007, 11:17
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 halt da wo Synchronize(BufferOnImage); steht
Richard Mahr
  Mit Zitat antworten Zitat
messie

Registriert seit: 2. Mär 2005
Ort: Göttingen
1.592 Beiträge
 
Delphi 2009 Professional
 
#4

Re: [Thread] Zeichnen hört teilweise auf

  Alt 8. Nov 2007, 11:17
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
  Mit Zitat antworten Zitat
Benutzerbild von Tormentor32
Tormentor32

Registriert seit: 27. Okt 2005
Ort: Düsseldorf
369 Beiträge
 
Delphi XE5 Professional
 
#5

Re: [Thread] Zeichnen hört teilweise auf

  Alt 8. Nov 2007, 12:20
Zitat von messie:
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.
Hmm einer von uns beiden liegt falsch, ich bin jetzt grad auch nicht mehr sicher... Ich dachte dass ich synchronize benutzen muss, wenn ich aus dem Thread heraus auf die nicht-threadsichere-VCL zugreife. Eben darum mach ich ja alles im Thread, der Buffer wird bemalt und dann über synchronize auf die VCL übertragen, das müsste imho so richtig sein...

Zitat von messie:
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.
Daran kann es nicht liegen, ich kriege die Werte ja immer noch rein, sonst würden sich Abb. zwei und die Labels ja nicht aktualisierenn und die Werte werden auch empfangen hab ich alles durch Breakpoints herausfinden können.

Zitat von messie:
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.
Ich glaube der Hinweis von Muetze1 war auf das Aktualisieren der Labels bezogen, welche ich nicht mit synchronize aktualisiert habe. Das habe ich jetzt testweise eingebaut, das Problem ist, dass das "Phänomen" so unregelmäßig auftritt, dass ich jetzt noch nicht sagen kann ob der Fehler weg ist. Wäre aber auch komisch imho, weil die labels ja funktionieren und das Image nicht. Image2 funktioniert ja trotzdem...

Zitat von messie:
Grüße, Messie
Tschüssi
Richard Mahr
  Mit Zitat antworten Zitat
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#6

Re: [Thread] Zeichnen hört teilweise auf

  Alt 8. Nov 2007, 12:32
Vielleicht solltest du dir das mal von Hagen durchlesen.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Muetze1
(Gast)

n/a Beiträge
 
#7

Re: [Thread] Zeichnen hört teilweise auf

  Alt 8. Nov 2007, 13:11
Zitat von Tormentor32:
Ich glaube der Hinweis von Muetze1 war auf das Aktualisieren der Labels bezogen, welche ich nicht mit synchronize aktualisiert habe. Das habe ich jetzt testweise eingebaut, das Problem ist, dass das "Phänomen" so unregelmäßig auftritt, dass ich jetzt noch nicht sagen kann ob der Fehler weg ist. Wäre aber auch komisch imho, weil die labels ja funktionieren und das Image nicht. Image2 funktioniert ja trotzdem...
Nein, grundsätzlich. Da schon der Zugriff auf viele Eigenschaften der VCL Objekte Nachrichten auslösen bzw. weitere Referenzen innerhalb der VCL benutzen, sollte es vermieden werden, diese innerhalb eines Threads zu nutzen. Grundlegend würde ich dir auch raten, dass du dir ein TBitmap innerhalb des Threads anlegst und im Synchronise dann Image1.Picture.Bitmap.Assign(fBitmap); nutzt. Damit hast du eine definitiv getrennte Instanz (von der Oberfläche...). Bei den Labels würde ich diese gar nicht erst in den Thread reingeben - wozu auch? Weise den Text synchronisiert zu, etc. Aber keine VCL Oberflächen Elemente innerhalb des Threads. Wenn du das grundlegend umsetzt, dann solltest du unter Nutzung von Synchronize() gar keine Probleme bekommen. Im jetzigen Zustand kann man gar nicht abwägen oder abschätzen was da von was abhängt und irgendwie mal Ärger macht. Schon allein, da sich fast jedes im Thread referenzierte Objekt anbietet für Probleme...
  Mit Zitat antworten Zitat
messie

Registriert seit: 2. Mär 2005
Ort: Göttingen
1.592 Beiträge
 
Delphi 2009 Professional
 
#8

Re: [Thread] Zeichnen hört teilweise auf

  Alt 8. Nov 2007, 15:21
Zitat von Delphi Help:
Synchronize causes the call specified by Method to be executed using the main VCL thread, thereby avoiding multi-thread conflicts. If you are unsure whether a method call is thread-safe, call it from within the Synchronize method to ensure that it executes in the main VCL thread.

Execution of the thread current is suspended while Method executes in the main VCL thread.
Ich gebe zu, daß die Hilfe da nicht sehr übersichtlich ist. Und das hier
Zitat von Delphi Help:
Delphi-Quellcode:
procedure TMyThread.PushTheButton;

begin
  Button1.Click;
end;
procedure TMyThread.Execute;
begin
  ...
  Synchronize(PushTheButton);
  ...
end;
halte ich für falsch, denn es würde einen TButton im Thread-Objekt voraussetzen. Eher so:
Delphi-Quellcode:
procedure TForm1.PushTheButton;
begin
  Button1.Click;
end;
procedure TMyThread.Execute;
begin
  ...
  Synchronize(Form1.PushTheButton);
  ...
end;


Grüße, Messie
  Mit Zitat antworten Zitat
Muetze1
(Gast)

n/a Beiträge
 
#9

Re: [Thread] Zeichnen hört teilweise auf

  Alt 8. Nov 2007, 18:06
Zitat von messie:
... halte ich für falsch, denn es würde einen TButton im Thread-Objekt voraussetzen. Eher so:
Das ist gehustet wie gesprungen. Es ist schliesslich nicht die gesamte Klasse welche den Thread stellt, sonder nur die Execute Methode welche innerhalb des Threads läuft. Wenn du nun mit Synchronize() eine Methode angibst, dann wird die Adresse der Methode per SendMessage in den VCL Thread (Message-Queue Bearbeitung) geschickt und dort dann aufgerufen. SendMessage kehrt nach der Ausführung mit dem Result zurück und in so fern wartet der Thread auf die Abarbeitung der angegebenen Routine (alles grob und vereinfacht).

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.
  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 10:57 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