Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Threads und Zugreifen auf Controls (https://www.delphipraxis.net/59786-threads-und-zugreifen-auf-controls.html)

Matze 28. Dez 2005 16:15


Threads und Zugreifen auf Controls
 
Hallo,

vorweg: Ich kenne dein Tutorial Luckie, nur möchte ich vorerst die Thread-Klasse der VCL nutzen, da das jetzt für mich übersichtlicher ist (es ist meien erste Anwendung, wo ich Threads verwende).

So, nun zu meinem Problem:
Ich parse einen Text im OnKeyDown eines Memos, was bei langem Text etwas dauern kann (1-2 Sek. vielleicht). Löse ich das ohne Thread, kann man logischerweise kaum tippen, daher dachte ich, ich lager das in einen extra Thread aus.

Den Thread möchte ich so in der Art aufrfen:

Delphi-Quellcode:
procedure TMyThread.Execute;
begin
  if myMemo <> nil then
  begin
    CalculateQuickbarArray;
    Synchronize(ParseQuickBar);
  end;
end;
CalculateQuickbarArray:

Delphi-Quellcode:
procedure TMyThread.CalculateQuickbarArray;
begin
  SetLength(RegExArray, 0);        

  ParseRegExpr(myMemo.Text, 'blubb', 4);
(ParseQuickBar gibt ein im CalculateQuickbarArray befülltes Array aus.)

Dass es so falsch ist, ist mir klar, da ich zwei mal außerhalb von Synchronize auf ein VCL Objekt zugreifen möchte. Einmal beim Vergleich, ob es existiert und einmal bei der Parameterübergabe.

Nur wenn ich das über Synchronize mache, friert meine Anwendung beim Ausführen des Threads ein, da ja alles Synchronosiert ist.

Wie umgehe ich das Problem?

Seltsam ist auch, dass das Execute-Ereignis ausgelöst wird, wenn ich besagten Thread mittels

Delphi-Quellcode:
myThread := TmyThread.Create(true);
starte. Eigentlich müsste der Thread ja nun pausiert sein.

Btw: Im OnKeyDown ist mein Code folgender:

Delphi-Quellcode:
myThread.Execute
Wahrscheinlich mache ich alles falsch, was man nur falsch machen kann. :zwinker:

jbg 28. Dez 2005 16:26

Re: Threads und Zugreifen auf Controls
 
Zitat:

Zitat von Matze
Btw: Im OnKeyDown ist mein Code folgender:

Delphi-Quellcode:
myThread.Execute

Und wofür brauchst du dann einen Thread, wenn du den Code direkt im OnKeyDown aufrufst?

Wäre es nicht besser einen Timer zu nehmen und im OnTimer Ereignis die Verarbeitung durchzuführen. Im OnKeyDown wird dann nur der Timer resettet:
Delphi-Quellcode:
OnKeyDown:
begin
  // Timer neustarten
  TimerCalc.Enabled := False;
  TimerCalc.Enabled := True;
end;

OnTimer: (Interval = 100)
begin
  Timer.Enabled := False; // nicht mehrmals aufrufen wenn nichts passiert ist
  CalculateQuickbarArray;
  ParseQuickbar;
end;
Dadurch kann der Benutzer tippen und erst wenn er stoppt oder langsam tippt, wird berechnet. Den Timer solltest du in OnEnter starten und OnExit auf den OnTimer-Handler setzen, damit beim verlassen des Controls der Timer abgeschatet und auch neu berechnet wird.

jim_raynor 28. Dez 2005 16:44

Re: Threads und Zugreifen auf Controls
 
Du müsstest du Thread den Text aus dem Memo mitgegeben. Dann verarbeitet der Thread diesen Text und du schreibst per Syncronize zurück, bzw. was auch immer damit gemacht werden soll. Aber die Lösung mit dem Timer hab ich auch in meinem Skript-Editor drin und funktioniert eigentlich ganz gut. Nur hab ich nicht 100ms sondern eine Sekunde, aber das hängt ja massiv vom Anwendungsfall ab.

Matze 28. Dez 2005 16:47

Re: Threads und Zugreifen auf Controls
 
Hallo

@Andreas: Die Idee ist zwar nicht schlecht, doch friert die Anwendung dann wieder komplett ein, während der Pars-Vorgang ausgeführt wird.

@Christian: Aber genau das mache ich doch, eben über den Parameter, der sich den Text direkt holt (was nicht geht). Oder was meinst du nun?

jim_raynor 28. Dez 2005 16:57

Re: Threads und Zugreifen auf Controls
 
Du definierst im Thread eine Private String-Variable und dazu ein passendes Property

Delphi-Quellcode:
TMyThread =class(TThread)
private
  fText: String;
public
  property Text: String write fText read fText;
end;
Im OnKeyDown weist du dann Text den String im Memo zu. Im Thread arbeitest du dann nur mit fText anstatt mit dem aus dem Memo. So brauchst du nicht auf die Daten des Threads in Syncronize zugreifen.

Matze 28. Dez 2005 17:12

Re: Threads und Zugreifen auf Controls
 
So in der Art habe ich es auch versucht, doch kam immer

Zitat:

[Fehler] UnitThreadQuickbar.pas(21): E2029 ';' erwartet, aber Bezeichner 'read' gefunden
was ich mir nicht erklären kann.

Die Muhkuh 28. Dez 2005 17:19

Re: Threads und Zugreifen auf Controls
 
Imho, musst du read und write umdrehen ;)

Matze 28. Dez 2005 17:28

Re: Threads und Zugreifen auf Controls
 
Zitat:

Zitat von Spider
Imho, musst du read und write umdrehen ;)

:wall:

Danke, das sieht nun schonmal sauberer aus, als ich es gemacht habe. Doch friert das ganze immer noch ein. Das liegt bestimmt daran, dass ich noch auf die Komponente im Vergleich zugreife (das könnte ich eigentlich auch über eine property und boolean lösen) oder am Synchronizise, was ich eher vermute.

Nachtrag: Ne, die boolsche Property geht doch nicht, da der Thread ja beim Programmstart mitgestartet wird.

jim_raynor 28. Dez 2005 18:49

Re: Threads und Zugreifen auf Controls
 
Kann es sein, dass dein Problem nicht das Parsen ist sondern der Aufbau der Liste im Formular (also das was du im Syncronize machst)?

jbg 28. Dez 2005 19:33

Re: Threads und Zugreifen auf Controls
 
Oder rufst du MyThread.Execute immernoch direkt auf?

Matze 28. Dez 2005 22:35

Re: Threads und Zugreifen auf Controls
 
Zitat:

Zitat von jim_raynor
Kann es sein, dass dein Problem nicht das Parsen ist sondern der Aufbau der Liste im Formular (also das was du im Syncronize machst)?

Das Parsen ist nicht das Problem (auch wennd as performanter ginge). Das Problem ist, mehr oder weniger, der Aufruf, damit das ganze nicht einfriert.

Zitat:

Zitat von jbg
Oder rufst du MyThread.Execute immernoch direkt auf?

Ähm ja, das mache ich so. Sonst bräuchte ich ja irgendwie eine Endlosschleife, die ich per Suspend und Resume steuern müsste, was mir nicht sehr professionel klang. Aber ich lasse mich gerne korrigieren.

jbg 29. Dez 2005 00:48

Re: Threads und Zugreifen auf Controls
 
Zitat:

Zitat von Matze
Ähm ja, das mache ich so.

Dann kannst du den Thread gleich in die Tonne schmeißen, wenn du ihn gar nicht nutzt.

Zitat:

Sonst bräuchte ich ja irgendwie eine Endlosschleife
Soll das heißen, dass du folgendes möchtest:
  • Benutzer drückt taste
  • Thread starten
  • Wenn Thread beendet ist, kann Benutzer weitere Taste drücken
Wenn du genau das willst, dann hast du Multithreading noch nicht verstanden. (was ich jetzt nicht hoffe)

Zitat:

Suspend
Suspend sollte man schon mal gar nicht benutzen, denn das kann zu Deadlocks führen, wenn man den Thread an einer ungünsitgen Stelle anhält (die POSIX Threads von Unix/Linux bietet ein Suspend schon mal von Haus aus gar nicht an).

Matze 29. Dez 2005 00:54

Re: Threads und Zugreifen auf Controls
 
Zitat:

Zitat von jbg
Wenn du genau das willst, dann hast du Multithreading noch nicht verstanden. (was ich jetzt nicht hoffe)

Nein, genau das möchte ich ja nicht. Der Anwender soll während dem Parsen schreiben können und das Parsen soll im Hintergrund, eben in einem extra Thread, geschehen.

Hier liest man so viel über Suspand, Resume, WaitFor aber irgendwie ist alles nichts rechtes.

jbg 29. Dez 2005 01:05

Re: Threads und Zugreifen auf Controls
 
Probiere es mal so (Code funktioniert natürlich nicht 1:1, ein wenig überlegen gehört auch zum Programmieren):
Delphi-Quellcode:
type
  TMyThread = class(TThread)
  private
    FText: string;
  protected
    procedure Execute; override;
  public
    constructor Create(const AText: string);
  end;

{ TMyThread }
constructor TMyThread.Create(const AText: string);
begin
  FText := AText; // initialisieren
  inherited Create(False); // und starten
  FreeOnTerminate := True;
end;

procedure TMyThread.Execute;
begin
  // FText ist hier nun verfügbar

  CalculateQuickbarArray(FText); // FText statt myMemo.Text an ParseRegExpr() übergeben
  Synchronize(ParseQuickBar); // ParseQuickBar sollte nicht mehr viel machen!
end;

...

type
  TForm1 = class(TForm)
  ...
  private
    FMyThread: TMyThread;
    procedure MyThreadTerminated(Sender: TObject);
  end;

procedure TForm1.MyThreadTerminated(Sender: TObject);
begin
  FMyThread := nil; // neuen Thread in MyMomeKeyDown() erlauben
end;

procedure TForm1.MyMemoKeyDown(...)
begin
  if not Assigned(FMyThread) then // wenn im Moment kein anderer Thread läuft ...
  begin
    FMyThread := TMyThread.Create(myMemo.Text); // ... einen neuen Thread starten
    FMyThread.OnTerminate := MyThreadTerminate;
  end;
end;

Matze 29. Dez 2005 01:17

Re: Threads und Zugreifen auf Controls
 
Hallo Andreas,

nun geht es deutlich besser als vorhin und ruckelt kaum noch. Das bisschen ligt nun sicher an der Funktion, die ich mit Synchronize aufrufe, das optimiere ich noch etwas. Vielen Dank. :thumb:

Zitat:

Zitat von jbg
[...] ein wenig überlegen gehört auch zum Programmieren

Das nehme ich persönlich. :-| Auch wenn man es nicht merkt, ich habe Stunden, wirklich Stunden gesucht und gelesen und bin nie auf deine Methode gestoßen.

alzaimar 29. Dez 2005 08:12

Re: Threads und Zugreifen auf Controls
 
Zitat:

Zitat von Matze
Zitat:

Zitat von jbg
[...] ein wenig überlegen gehört auch zum Programmieren

Das nehme ich persönlich. :-| Auch wenn man es nicht merkt, ich habe Stunden, wirklich Stunden gesucht und gelesen und bin nie auf deine Methode gestoßen.

Wieso hast Du nicht die Online-Hilfe gelesen? Die paar Beispiele im Demo-Verzeichnis von Delphi reichen doch auch aus. Im Übrigen dürfte sich seine Anmerkung auf den nicht getesteten Code beziehen, den man nicht 1:1 übernehmen sollte, weil eben "ein wenig Überlegen" auch "zum Programmieren" gehört. Ehrlich gesagt frage ich mich, wo du gelesen hast. Es gibt übrigens noch andere Quellen außer DP.

So, nun zum Fachlichen:
Du solltest einen Workerthread erzeugen. Der wartet, bis etwas zu tun ist, verrichtet seine Arbeit und wartet dann wieder.
Delphi-Quellcode:
Procedure TWorkerThread.Execute;
Begin
  While Not Terminated Do
    If WaitForSingleObject (fMySemaphore,nil,nil, 500) = WAIT_OBJECT_0 do
      DoSomething
End;
Im 'DoSomething' wird dann dein Text verwurstet o.ä.
Zitat:

Zitat von Matze
Hier liest man so viel über Suspand, Resume, WaitFor aber irgendwie ist alles nichts rechtes.

Das 'WaitForSingleObject' ist Rechtens. Es ist die von Windows empfohlene Methode, weil sie, laut Online-Hilfe (Du erinnerst Dich? :zwinker: ), sehr effektiv ist. Beim Warten wird also so gut wie keine CPU-Zeit verbraten. Das ist also, im Gegensatz zu deinem Gefühl, sehr professionell.

Das 'fMySemaphore' ist eine Semaphore, die man zur Kommunikation mit Threads verwenden kann: Eine Semaphore ist sowas wie ein threadsicher Zähler, und WaitForSingleObject wartet drauf, das der Zähler <> 0 ist. Dann wird der Zähler um 1 verringert und die Funktion verlassen (Steht alles in der OH). Wenn man will, das der Workerthread arbeiten soll, muss man nur die Semaphore hochzählen. Das geht dann mit 'ReleaseSemaphore', die die Semaphore hochzählt. So kann man -auch über Prozessgrenzen hinweg- einen Thread anstossen.

Um dem Thread also mitzuteilen, das er arbeiten soll, rufst Du einfach:
Delphi-Quellcode:
ReleaseSemaphore (fMySemaphore,1,Nil)
auf. Eine Semaphore wird z.B. so erzeugt (schau in der Win32-Hilfe nach der Bedeutung der Parameter):
Delphi-Quellcode:
fMySemaphore := CreateSemaphore(NIL, 0, 1, NIL);
Da es sich um ein Handle handelt :stupid:, weisst Du dann auch, wie man es wieder freigibt.

Wesentlich resourcenschonender, sozusagen die Leichtgewichtsvariante, ist eine 'Critical Section', die, man ahnt es kaum, in der OH sehr ausführlich beschrieben wird. Mit Suspend und Resume kann man einen Thread auch steuern, aber *das* ist dann nicht sehr professionell.

Matze 29. Dez 2005 08:50

Re: Threads und Zugreifen auf Controls
 
Hallo alzaimar,

Ich habe gegoogelt, was das zeug hält aber du hast Recht, in die OH habe ich nicht geschaut. :oops:

Danke für deine Erläuterungen, mal testen, was da nun performanter ist.


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