Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   warum verlangsamen diese beiden Funktionen meinen Thread? (https://www.delphipraxis.net/140756-warum-verlangsamen-diese-beiden-funktionen-meinen-thread.html)

BAMatze 25. Sep 2009 12:54


warum verlangsamen diese beiden Funktionen meinen Thread?
 
Hallo und guten Tag an alle DP´ler

Mal wieder eine kleine Verständnisfrage. Und zwar werden einige Änderungen am ComPorts überwacht und sollen auf einer Form dargestellt werden. Dies funktioniert zumindest auch, allerdings verlangsamt dies mein Programm erheblich und blockiert es zumindest für 2-3 Sekunden (z.B. bei einem Klick auf der Form ändert sich das Checked einer Checkbox nicht sofort.

Hier mal alle relevanten Funktionen. Die Refresh der Formen (welche ich denke, dass sie mich am meisten behindern, hab ich schon versucht mit hilfe von boolschen Variablen deutlich zu verringern):

Delphi-Quellcode:
procedure TControlCardControlThread.Execute;
var c, iIndex: integer;
begin
  for iIndex := 1 to 5 do (Form3.findcomponent('CheckBox' + inttostr(iIndex+8)) as TCheckBox).Checked:= fabDigChannelSignals[iIndex];
  repeat
  c := GetTickCount;
  Synchronize(UpdateForm3);
  c := {interval}50 - (GetTickCount - c);
  if c > 0 then Sleep(c);
until Terminated;
if assigned(FsLComport) then FsLComport.Free;
end;

{////////////////////////////////////////////////////////////////////////////////////}
{/                           Interaktion mit Oberfläche                            /}
{////////////////////////////////////////////////////////////////////////////////////}

// bei dieser Form sollte festgestellt werden, ob sich irgendwas verändert hat und
// nur bei vorhandenen Änderungen sollte auch Refreshed werden. Ständiges neuzeichnen
// führt zum ausbremsen des TThreads!
procedure TControlCardControlThread.UpdateForm3;
var i: integer;
    bGeneralDigChannelInput, bCriticalDigChannelInput, bUpdateDataForm1, bUpdateDataForm3: boolean;
begin

  bUpdateDataForm1 := ComportSearch(FsLComport);

  bUpdateDataForm3 := false;
  bgeneralDigChannelInput := false;
  bcriticalDigChannelInput := false;
  for i := 1 to 5 do
    begin
      case not(FabDigChannelSignals[i] xor Board.digEingang[i]) of
      false: begin
               bUpdateDataForm3 := true;
               DoSingleDigChannelInput(i);
               bGeneralDigChannelInput := true;
               if i in [2..4] then bcriticalDigChannelInput := true;
               FabDigChannelSignals[i] := Board.digEingang[i];
               (Form3.findcomponent('CheckBox' + inttostr(i+8)) as TCheckBox).Checked:= fabDigChannelSignals[i];
             end;
      end;
     
      if (i in [1..2]) and ((Form3.findcomponent('ProgressBar' + inttostr(i)) as TProgressBar).Position <> Board.anaEingang[i]) then
        begin
          (Form3.findcomponent('ProgressBar' + inttostr(i)) as TProgressBar).Position := Board.anaEingang[i];
          bUpdateDataForm3 := true;
        end;
    end;
  if bUpdateDataForm3 then Form3.Refresh; // <-- Änderung nur refresh, wenn Änderung vorhanden sind
  if FbBlinkRedLight then Board.RedLight := not Board.RedLight;
  if bGeneralDigChannelInput then doGeneralDigChannelInput;
  if bCriticalDigChannelInput then doCriticalDigChannelInput;
  // Hier ebenso Änderungen überprüfen und nur bei vorhandenen Änderungen das neuzeichnen
  // auslösen!
  if bUpdateDataForm1 then
    begin
      Form1.Memo1.Clear;
      Form1.Memo1.Lines := FsLComport;
      Form1.Refresh;
    end;

  case Counter of
  0: Form3.StatusBar1.Panels[0].Text := '/';
  1: Form3.StatusBar1.Panels[0].Text := '|';
  2: Form3.StatusBar1.Panels[0].Text := '\';
  3: Form3.StatusBar1.Panels[0].Text := '-';
  end;
  Counter := Counter + 1;
  if Counter > 3 then Counter := 0;
end;

{////////////////////////////////////////////////////////////////////////////////////}
{/                              Überwachungsfunktion                               /}
{////////////////////////////////////////////////////////////////////////////////////}

// In eine Funktion umarbeiten, die true zurückgibt, wenn keine Änderungen vorhanden sind
// oder false wenn Änderungen vorhanden sind.
function TControlCardControlThread.ComportSearch(var List: TStringList):boolean;
var ComBuffer: TComport;
    TempLineArray: array of Integer;
    bLine: boolean;
    i, j: integer;
begin
  ComBuffer := TComport.Create;
  result := false;
  try
    SetLength(TempLineArray,0);
    for i := 0 to ComBuffer.Anzahl - 1 do
      begin
        j := List.IndexOf(inttostr(ComBuffer.Comportnummer[i]));
        case j of
        -1: begin
              result := true;
              List.Add(inttostr(ComBuffer.Comportnummer[i])); // Eintrag ist noch nicht vorhanden -> Eintrag wird hinzugefügt
              SetLength(TempLineArray,Length(TempLineArray)+1);
              TempLineArray[Length(TempLineArray)-1] := List.IndexOf(inttostr(ComBuffer.Comportnummer[i]));
            end;
        else
          begin
            SetLength(TempLineArray,Length(TempLineArray)+1);
            TempLineArray[Length(TempLineArray)-1] := j;
          end;
        end;
      end;
    //in Temp Linearray sind jetzt alle Zeilen Nummern der StringList enthalten, die
    //auch in der Comport-Liste vorhanden sind. Nun müssen die überflüsigen Zeilen nur gelöscht werden
    for i := 0 to List.Count -1 do
      begin
        bLine := false;
        for j := 0 to Length(TempLineArray) do if i= TempLineArray[j] then bLine := true; // Zeile ist in TempLineArray vorhanden und soll somit NICHT gelöscht werden!
        if not bLine then
          begin
            List.Delete(i); //Wenn die Zeile nich in TEmpLineArray vorhanden ist, muss der Comport geschlossen worden sein und kann somit aus der Liste gelöscht werden.
            result := true;
          end;
      end;
  finally
    SetLength(TempLineArray,0);
    ComBuffer.Free;
  end;
end;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Hier noch die Comport-Klasse:

Type TComport = class
  private
    bPorts: boolean;
    FsLComports: TStringList;
    function ComPort(ComPortNummer: byte): longbool;
    function ComportScan: boolean;
    function GetComport(index: integer): integer;
    function GetAnzahl: integer;
  public
    constructor Create; reintroduce;
    destructor destroy; override;
    property verfuegbarePorts: boolean read bPorts;
    property Comportnummer[Index: integer]: integer read GetComport;
    property Anzahl: integer read GetAnzahl;
end;

Constructor TComport.Create;
begin
  inherited create;
  FsLComports := TStringlist.Create;
  bPorts := ComportScan;
end;

destructor TComport.Destroy;
begin

  FsLComports.Free;
  inherited destroy;
end;

function TComport.GetComport(index: Integer): integer;
begin
  result := strtoint(FsLComports[Index]);
end;

function TComport.GetAnzahl;
begin
  result := FsLComports.Count;
end;

// Funktion, die überprüfft, ob an der Comschnittstelle mit der Nummer "ComPortNummer"
// ein Gerät angeschlossen ist (ob eine Kommunikation geöffnet ist) oder nicht.
function TComport.ComPort(ComPortNummer: byte): longbool;
var TestHandle : integer;
begin TestHandle :=CreateFile(PChar('\\.\COM'+IntToStr(ComPortNummer)),GENERIC_READ or GENERIC_WRITE,0,
                              nil,OPEN_EXISTING,FILE_FLAG_OVERLAPPED,LongInt(0));
  if (TestHandle <= 0) then Result := false
  else begin Result := true; CloseHandle(TestHandle); end;
end;
// Funktion, die alle ComPorts bis zu einer vom Programmierer festgelegten maximal
// ComPortnummer anspricht.
function TComport.ComportScan: boolean;
var i: integer;
    aBuffer: array of Integer;
begin
  for i := 0 to 20 do if ComPort(i) then FsLComports.Add(inttostr(i));
  if FsLComports.Count = 0 then result := false
  else result := true;
end;
Also der Fehler sollte immer meiner Meinung nach immer noch daran liegen, dass er zuviel macht, sehe aber gerade nicht, wo ich die Interaktion zwischen Thread und Formen noch NICHT eingeschränkt habe, wenn keine Änderung vorliegt.

Hoffe ihr könnt mir wieder helfen
BAMatze

Tryer 25. Sep 2009 13:09

Re: warum verlangsamen diese beiden Funktionen meinen Thread
 
Also mit fällt nichts besonderes auf, darum nur ein paar Ideen:

- FindComponent kannst Du Dir sparen wenn Du eine eigene Liste(/Array) der relevanten Controls führst.

- Der Thread sollte keine erhöhte Priorität haben, da er durch das Synchronize eh immer wieder ausgebremst wird und auf den Hauptthread wartet. Wenn der Hauptthread nicht dazu kommt Botschaften zu verarbeiten kann Synchronize ewig warten.

- Reagiert der Thread schneller, wenn du mit der Maus auf dem Formular herumeierst? Dann hilft das Umbiegen von WakeMainThread (hatte ich ja schonmal vorgeschlagen).

[EDIT] - Wieviel Zeit verbringt der Thread im Sleep? [/Edit]

Grüsse, Dirk

hugo1990 25. Sep 2009 13:14

Re: warum verlangsamen diese beiden Funktionen meinen Thread
 
Hallo,
ich würde mal sagen dein Problem liegt hier:
Delphi-Quellcode:
  Synchronize(UpdateForm3);
So wie es aussieht, machst du die ganzen Prüfungen, ob ich etwas an den COM-Ports geändert hat, in der UpdateForm3. Diese Methode lässt du aber mit Synchronize aufrufen, was bewirkt, dass der Thread des Formulars solange angehalten wird, bis die Abarbeitung der UpdateForm3 abgeschlossen ist.
Pack doch diese ganzen Checks in eine andere Methode oder einfach in die Execute Methode und schreib in die UpdateForm3 nur rein, was an der Form3 geändert werden soll. Und dann rufst du die UpdateForm3 mit Synchronize nur dann auf, wenn sich wirklich was geändert hat.
So wie du es gerade implementiert hast, ist der Thread ziemlich nutzlos.

Ich hoffe, dass ich es einiermaßen verständlich erklärt habe.

[edit]
Ok ich habe grad nochmal die Delphihilfe angeschaut und Synchronize bewirkt, dass die Methode im Hauptthread ausgeführt wird und der aktuelle Thread für die Zeit der Ausführung unterbrochen wird. Es kommt im Endeffekt aber auf das selbe raus, dass der Hauptthread während der Abarbeitung der UpdateForm3 nicht auf andere Sachen reagieren kann.
[/edit]

BAMatze 25. Sep 2009 13:26

Re: warum verlangsamen diese beiden Funktionen meinen Thread
 
Ok werde ich mal versuchen umzusetzen. Danke euch

[Edit]
Folgende kleine Änderung hat die Schnelligkeit des Threads wieder zu 100% hergestellt (orientiert an dem Lösungsvorschlag von hugo1990):
Delphi-Quellcode:
// bUpdateDataForm1 ist jetzt eine private-Variable des TThreads
procedure TControlCardControlThread.Execute;
var c, iIndex: integer;
begin
  for iIndex := 1 to 5 do (Form3.findcomponent('CheckBox' + inttostr(iIndex+8)) as TCheckBox).Checked:= fabDigChannelSignals[iIndex];
  repeat
  c := GetTickCount;
  bUpdateDataForm1 := ComportSearch(FsLComport); // <-- Zeile aus der UpdateForm3-Procedure entfernt und hier wieder eingefügt
  Synchronize(UpdateForm3);
  c := {interval}50 - (GetTickCount - c);
  if c > 0 then Sleep(c);
until Terminated;
if assigned(FsLComport) then FsLComport.Free;
end;
[/Edit]

Zoot 25. Sep 2009 14:36

Re: warum verlangsamen diese beiden Funktionen meinen Thread
 
Verstehe aber auch nicht so ganz, warum du dir die vorhandenen Comports in einer Stringliste merkst.
Wäre da nicht ein Statischer Array von der Art Comportaktiv : Array [1..20] of boolean viel einfacher?

alzaimar 25. Sep 2009 15:02

Re: warum verlangsamen diese beiden Funktionen meinen Thread
 
Noch eine Anregung, um ein Performanceproblemen bei Verwendung von Synchronize zu umgehen: Verwende Windows-Messages, die das Formular veranlassen, die Threadinformationen neu zu zeichnen.

Dem Thread übergibst du das Handle des Formulars und ruft dann einfach im Thread 'PostMessage(HandleDerForm...)' auf.


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:11 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