Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Thread mit timer gestartet (https://www.delphipraxis.net/182462-thread-mit-timer-gestartet.html)

akurka 27. Okt 2014 11:01

Thread mit timer gestartet
 
Hallo
Mein Thread liest Daten von der Comport und schreibt sie in verschiedene globale Variablen.
Das funktioniert mit einer endlose Schleife im Thread.
Das ist aber sicher nicht die beste Lösung.
Ich möchte lieber, dass es periodisch alle 200mS passiert.
Wenn ich mit der Componente Timer (gestartet im Form1)
das Thread starte, funktioniert es nur so lang
Form1 aktiv ist.
Beim Wechsel in Form2 läuft der Timer nicht mehr und somit wird auch der Thread nicht gestartet.
Ja nun, wie lässt es sich machen, dass sich der Thread
alle 200mS selber startet und zwahr unabhängig davon
in welche Form x ich mich gerade befinde?
Im meinem Programm wird es vermutlich 8 verschiedene
Menüs geben => jeweils andere Forms.
Und wie lässt es sich machen das möglichst wenig Rechenzeit verbraucht wird während der Wartezeit ?
Eine endlose schleife mit sleep im thread-execute ist vermutlich nicht
die beste Lösung, oder ?

DeddyH 27. Okt 2014 11:05

AW: Thread mit timer gestartet
 
Zitat:

Zitat von akurka (Beitrag 1277493)
Das ist aber sicher nicht die beste Lösung.

Und wieso nicht? Wenn Du nicht willst, dass der Thread permanent Daten einliest, schick ihn halt eine Weile schlafen, das dürfte allemal besser als diese Timer-Lösung sein.

Bowler 27. Okt 2014 11:33

AW: Thread mit timer gestartet
 
Der Timer braucht ja ein Window-Handle mit dem er dann läuft. Deswegen scheidet die Möglichkeit schonmal aus, einen Timer im Thread zu erstellen.
Ich habe das in einem Projekt bei mir genauso gemacht. In einer Schleife im Thread.Execute wird eine Funktion aufgerufen, die z.B. Daten einliest, und wenn die Funktion durch ist, dann rufe ich ein Sleep auf, und warte einige Zeit. Wenn das Sleep zurück kommt, dann läuft die Schleife weiter. Und damit wäre Dein Thread autark und läuft eigenständig in einer Schleife.

Irgendwie sowas:

Delphi-Quellcode:

procedure TMainThread.Execute;
begin
     FreeOnTerminate := False;
     Try
        While Not Terminated do
        Begin
             meineEinleseFunktion; // <---- Hier wird die Funktion aufgerufen, die die Daten einlesen soll
             Sleep(5000);
        End;
     Except
        On E:Exception do ; // TODO: Exception logging...
     End;
     Terminate;
end;

Blup 27. Okt 2014 12:19

AW: Thread mit timer gestartet
 
Es gibt sicher bessere Lösungen, aber ohne das Programm jetzt komplett umzustellen könnte man den Timer einfach in ein Datenmodul verschieben.
Das lässt man beim Programmstart erstellen und ist immer da, egal welche Formulare angezeigt werden.

Dejan Vu 27. Okt 2014 13:44

AW: Thread mit timer gestartet
 
Die Idee ist doch 'alle x ms Daten zu lesen'. Was spricht dann gegen das 'Sleep' in der Bearbeitungsschleife, wie von Bowler skizziert?

EWeiss 27. Okt 2014 13:57

AW: Thread mit timer gestartet
 
Dafür gibt es in der Kernel32 die API SetWaitableTimer
Diesen Timer kann man dann innerhalb Thread.Execute erstellen.
Sleep ist immer eine schlechte Idee.

gruss

Neutral General 27. Okt 2014 14:07

AW: Thread mit timer gestartet
 
Warum ist sleep eine schlechte Idee?
Wenns nicht der Hauptthread ist dann friert nichts ein und während dem sleep kann sich der Prozessor um andere Prozesse/Threads kümmern wodurch Resourcen gespart werden.

EWeiss 27. Okt 2014 14:16

AW: Thread mit timer gestartet
 
Zitat:

Zitat von Neutral General (Beitrag 1277525)
Warum ist sleep eine schlechte Idee?

Weil Sleep die letzte aller Möglichkeiten sein soll wenn man es nicht besser weis.
Ich verwende lieber meine Methode da ich dann von zeit zu zeit Einfluss nehmen kann auf den Thread, wann eine Aktion ausgeführt werden soll.
Nicht jede Form oder Aktion benötigt eine Wartezeit von 200ms.
Das bremst unterumständen das Programm aus.

PS:
Wenn innerhalb des Threads auf Funktionen der GUI (Form) zugegriffen wird friert das Programm für 200ms ein.

Aber jeder wie er will.

gruss

Dejan Vu 27. Okt 2014 14:42

AW: Thread mit timer gestartet
 
Zitat:

Zitat von EWeiss (Beitrag 1277528)
Das bremst unterumständen das Programm aus....Wenn innerhalb des Threads auf Funktionen der GUI (Form) zugegriffen wird friert das Programm für 200ms ein.

Wenn Sleep innerhalb eines Threads ausgeführt wird?? Das will mir nicht so recht einleuchten.

EWeiss 27. Okt 2014 14:49

AW: Thread mit timer gestartet
 
Zitat:

Zitat von Dejan Vu (Beitrag 1277531)
Zitat:

Zitat von EWeiss (Beitrag 1277528)
Das bremst unterumständen das Programm aus....Wenn innerhalb des Threads auf Funktionen der GUI (Form) zugegriffen wird friert das Programm für 200ms ein.

Wenn Sleep innerhalb eines Threads ausgeführt wird?? Das will mir nicht so recht einleuchten.

Hauptthread ist dieser der GUI
Dein Thread hat nichts mit dem der GUI gemein.
Jetzt versuche mal aus deinen Thread einen Label aus der GUI anzusprechen so das er alle 10ms die neue Zeit anzeigt.
Ich denke das dein Thread das Programm einfriert und der Label in dem Fall nicht aktualisiert wird.
Versuchs doch einfach.

gruss

Neutral General 27. Okt 2014 14:51

AW: Thread mit timer gestartet
 
Delphi-Quellcode:
while true do
begin
  Synchronize(LabelSetzen);
  sleep(10);
end;
im Thread und ich bin ziemlich sicher dass es gehn wird :wink:
Edit: Ausprobiert - klappt ohne einzufrieren oder sonstige Probleme.

EWeiss 27. Okt 2014 15:00

AW: Thread mit timer gestartet
 
Na gut ich kenn es anders..
GUI-Elemente dürfen nur im Hauptthread erzeugt und angesprochen werden.. siehe Invoke!
Alles andere friert den Hauptthread ein.
Aber vielleicht verwechsle ich da auch was.:!:

War auch nur ein Vorschlag ob er nun Sleep verwendet oder meinen Vorschlag sei ihm überlassen.
Bei mir ist Sleep die allerletzte Entscheidung wenn es nicht mehr anders geht.

gruss

Neutral General 27. Okt 2014 15:09

AW: Thread mit timer gestartet
 
Naja wenn man ohne Synchronize auf die GUI des Hauptthreads zugreift kann es zu Fehlern und ggf. Absturz des Nicht-Hauptthreads führen falls beide Threads plötzlich gleichzeitig auf GUI-Elemente zugreifen. Es friert eigentlich nichts ein. Wäre das sleep(10) nicht im Thread dann würde der Main Thread einfrieren weil Synchronize die entsprechende Methode im MainThread ausführt und das den Thread quasi unnötig machen würde. So führt er das setzen des Labels im MainThread aus, kehrt zurück, wartet 10 MilliSekunden und wiederholt das ganze. Ist also in dem Fall einfach nur eine Art übermäßig komplizierter Timer.

EWeiss 27. Okt 2014 15:13

AW: Thread mit timer gestartet
 
Zitat:

wartet 10 Sekunden und wiederholt das ganze
OK ;)
Aber du meinst wohl 10ms.. Flüchtigkeitsfehler. :)

gruss

Neutral General 27. Okt 2014 15:21

AW: Thread mit timer gestartet
 
Eh ja genau, habs korrigiert :mrgreen:

EWeiss 27. Okt 2014 15:23

AW: Thread mit timer gestartet
 
Zitat:

Zitat von Neutral General (Beitrag 1277549)
Eh ja genau, habs korrigiert :mrgreen:

:thumb:
Ok das musste sein ;)

gruss

DeddyH 27. Okt 2014 15:24

AW: Thread mit timer gestartet
 
Man hätte auch einfach den Code ändern können ;)
Delphi-Quellcode:
sleep(10000);

EWeiss 27. Okt 2014 15:25

AW: Thread mit timer gestartet
 
Zitat:

Zitat von DeddyH (Beitrag 1277553)
Man hätte auch einfach den Code ändern können ;)
Delphi-Quellcode:
sleep(10000);

LOL

gruss

akurka 27. Okt 2014 15:34

AW: Thread mit timer gestartet
 
Hallo,
Danke für die viele Beiträge.
Wenn ich Euere Diskusion richtig verstanden habe, darf man den sleep verwenden wenn
man im Thread keine GUI funktionen braucht. Das ist auch bei mir der Fall.
Sonst muss man die API funktion benützen, ist noch wichtig das zu wissen !

Medium 27. Okt 2014 16:10

AW: Thread mit timer gestartet
 
Nicht ganz. Sleep geht immer. Auf die GUI sollte man eigentlich überhaupt nicht aus einem Thread zugreifen, wenn alle Stricke reißen aber bitte dann synchronisiert. Besser aber gar nie und nimmer, das zwingt einen auch gleichzeitig zu einem vernünftig entkoppelten Programmdesign. Ich nutze Sleep() auch sehr viel in Threads, und halte es für eine absolut gängige Methode wenn sichergestellt ist, dass man seinen Thread nicht doch event-gesteuert auch mal zwischen durch beleben möchte. (Wobei ich es auch wieder in die Design-Problem Ecke stellen würde, wenn man EINEN Thread hat, der sowohl zyklisch als auch nach Ereignissen reagieren soll.)
Dass ein Sleep nicht mit in den synchronisierten Abschnitt gehört, sollte eigentlich klar sein. Ansonsten: Schlaf gut!

Dejan Vu 27. Okt 2014 16:13

AW: Thread mit timer gestartet
 
Nein. Du kannst Sleep in einem Thread verwenden, auch wenn Du (natürlich indirekt, z.B. über Synchronize) auf GUI-Elemente aus dem Thread heraus zugreifst. Das macht überhaupt nichts.

Na ja, und so ein kleiner Backgroundworker in einem LOB-Tool, was man so mal eben schreibt, verletzt nur heimlich irgendwelche Designregeln. So zum runterschruppeln eines kleinen Tools ist das vollkommen ok.

haentschman 27. Okt 2014 17:12

AW: Thread mit timer gestartet
 
Hallo alle... :hi:
Ein Sleep im Thread ist zwar möglich, behindert im Normalfall auch nicht den Mainthread. Die Spreu trennt sich beim Beenden des MainThreads vom Weizen. Da wartet der Mainthread auf die Beendigung des Sleep des Threads...und der Benutzer wundert sich. :P

Lektüre für selbiges Problem:
http://www.delphipraxis.net/181596-p...m-tthread.html
einfache Lösung in #4

EWeiss 27. Okt 2014 17:18

AW: Thread mit timer gestartet
 
Zitat:

Zitat von haentschman (Beitrag 1277577)
Hallo alle... :hi:
Ein Sleep im Thread ist zwar möglich, behindert im Normalfall auch nicht den Mainthread. Die Spreu trennt sich beim Beenden des MainThreads vom Weizen. Da wartet der Mainthread auf die Beendigung des Sleep des Threads...und der Benutzer wundert sich. :P

Lektüre für selbiges Problem:
http://www.delphipraxis.net/181596-p...m-tthread.html
einfache Lösung in #4

Kann man nicht Verallgemeinern..
Wie Medium schon schrieb!
Zitat:

Wobei ich es auch wieder in die Design-Problem Ecke stellen würde
Ist also von Fall zu Fall Unterschiedlich je nach gebrauch.
Ich würde es grundsätzlich verneinen. (Zumindest in meinem Fall)

gruss

haentschman 27. Okt 2014 17:28

AW: Thread mit timer gestartet
 
Sinngemäß:

Delphi-Quellcode:
ThreadExecute;
while not Terminated then
begin
  // mach was
  Sleep(50000);
end;
Delphi-Quellcode:
procedure Form1.FormClose;
begin
  Thread.Terminate;
  Thread.Waitfor;
  Thread.Free; // wo auch immer.
end;
Frage: Wie lange dauert das Beenden?

PS: Wir sprechen nicht über Abwürgen sondern definiert Beenden. :zwinker:

EWeiss 27. Okt 2014 17:54

AW: Thread mit timer gestartet
 
Geht irgendwie am Thema vorbei.
Ich sagte doch Verwendungsabhängig.

Ich habe ein Plugin System in dem die Plugins selbst das verhalten und Aktualisierung des Threads beeinflussen.
Was soll ich da mit einem Sleep? Das blockiert nur die Anwendung.
Die Plugins legen also fest mit welchen Intervall der Thread aktualisiert (Timer abhängig).
Wie das bei ihm ist entzieht sich meiner Kenntnis aber verallgemeinern kann man das nicht.

Sleep or not Sleep das ist hier die Frage! ;)

gruss

Medium 27. Okt 2014 18:04

AW: Thread mit timer gestartet
 
Gut, in meinen Fällen bewegt sich die Wartezeit selten über 1000ms. Das ist in der Tat ein valides Problem. Aber auch "schmutzig" lösbar:
Delphi-Quellcode:
procedure TMyThread.Execute;
var
  timer: Integer;
begin
  repeat
    // Zeug
    timer := 0;
    repeat
      Sleep(100);
      inc(timer, 100);
    until (timer >= 1000) or Terminated;
  until Terminated;
end;
Nicht schön, aber möglich.

In meinem letzten Thread-intensiven Projekt habe ich es noch ein wenig anders: Es gibt einen Haupt-Workerthread, dessen Aufgabe es ist Jobs abzuarbeiten (Einträge in eine DB machen). Die Jobs fallen zyklisch an, wobei es mehrere verschiedene Zyklus-Gruppen gibt, die alle unterschiedliche Intervalle haben. Für jede Gruppe gibt es daher einen weiteren Thread, der einfach nur seine Wartezeit durch wartet, und dann dem Job-Thread signalisiert einen Job zu machen.
Delphi-Quellcode:
// "Timer-Thread"
proceudre TJobGroup.Execute;
begin
  repeat
    while ((GetTickCount-LastJobFired) < Interval) and (not Terminated) do
      Sleep(0);
    if not Terminated then
      SignalJob();
    LastJobFired := GetTickCount;
  until Terminated;
end;

// Job-Thread
procedure TJobs.Execute;
begin
  repeat
    while JobList.Count > 0 do
    begin
      DoStuff(JobList[0]);
      JobList.Delete(0);
    end;
    Sleep(0);
  until Terminated;
end;
(Das Sleep(0) sorgt dafür, dass der Thread immer mal wieder seine Time-Slices abgibt, und damit nicht immer maximale CPU Auslastung verursacht.)

Die JobList Zugriffe müssen natürlich durch eine CriticalSection gesichert werden. Aber das ist schon für ein recht großes Projekt, für ein Töölchen so nebenbei würde ich das so auch nicht machen.

akurka 29. Okt 2014 16:11

AW: Thread mit timer gestartet
 
Hallo
Ich habe offenbar mit meinem Thema eine kontroverse Diskusion angeworfen.
Besten Dank für die vielen Beiträge, ich habe daraus einiges gelernt.
Die Verwendung von "sleep" in Threads ist u.U. doch nicht ganz harmlos.
Gruss Anton


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