![]() |
2 Threads starten/pausieren...
Hallo,
ich habe derzeit einen Thread den ich folgendermaßen aufrufe.
Code:
An einer späteren Stelle mache ich dann Thread1.Resume.
Thread1 := TSelfThread.Create(True);
Thread1.FreeOnTerminate := True; In der Execute Prozedur gehe ich eine For-Schleife durch. Wenn die fertig ist, beendet sich der Thread ja automatisch. Solange der erste Thread läuft (also die For-Schleife noch nicht ferig ist), bräuchte ich eigentlich einen zweiten Thread, der evtl. ab und zu gestartet werden muss. Wenn der zweite Thread gestartet wird, soll sich der erste Thread kurz schlafen legen, also die For-Schleife soll auch pause machen. Wenn der zweite Thread (hier wird es auch eine For-Schleife geben) fertig ist, soll der erste Thread und die For-Schleife weiter laufen. Der zweite Thread muss nie pausiert werden, eher vorzeitig beendet und wieder neu gestartet werden, um hier andere Werte (von - bis) bei der For-Schleife durchzugehen. Wenn der erste Thread (also die For-Schleife) komplett fertig ist, muss der zweiter Thread nicht mehr gestartet werden. Kann man das realisieren? Michael |
AW: 2 Threads starten/pausieren...
Zitat:
|
AW: 2 Threads starten/pausieren...
Also ich möchte im Thread wenige Prüfungen machen, damit er schnell bleibt.
Ich habe im Thread beispielsweise eine For-Schleife von 0-5000, in der ein Programmcode abgearbeitet werden muss. Für die aktuelle For-Zahl kann ich jedenfalls prüfen, ob der Programmcode auch wirklich abgearbeitet werden muss (also er müsste eigentlich nicht beendet werden), denn je nach Benutzeraktion muss evtl. vorzeitig 250-500 oder 3000-4000 von der For-Schleife verarbeitet werden. Ich muss eigentlich im Hauptprogramm je nach Benutzeraktion zwei Variablen "von" und "bis" festlegen, die etwas Zeit benötigen und dachte deshalb an einen zweiten Thread. Michael |
AW: 2 Threads starten/pausieren...
Ich habe mir gerade folgendes für die Execute überlegt (ungetestet).
Müsste nur dann im Hauptprogramm wenn erforderlich die Variablen "von", "bis" und "check" setzen. Während meine 5000 (hier jetzt repeat/until) abgearbeitet werden, wird ja geprüft ob vorzeitig ein anderer Zahlen-Abschnitt gebraucht wird. Man muss nur schauen, dass man keine i-Zahl auslässt und eigentlich schon abgearbeitete nicht mehr erneut abgearbeitet werden.
Code:
Kann ich die Variablen "von", "bis" und "check" einfach setzen?
repeat
i = i +1; if check = true then begin if isave = -1 then begin isave = i; i = von; end; end; daten[i]... if check = true then begin if i = bis then begin i := isave; check := false; end; end; if i=5001 then ok := true; until ok=true So vielleicht:
Code:
Und im Hauptprogramm, während der Thread läuft dann irgendwo wenn gewünscht einfach...
type
TThread1 = class(TThread) private von,bis : integer; check : boolean; public procedure Execute; override; end;
Code:
Michael
Thread1.von := 50;
Thread1.bis := 100; Thread1.check := true; |
AW: 2 Threads starten/pausieren...
Und wieso benutzt du dann nicht einfach wei unterschiedliche Routinen für den selben Thread? So bekommst du es dann ohne Überpfüfungen hin...
Delphi-Quellcode:
EDIT: Kannst du natürlich auch mit anonymen Methoden machen, falls dein Delphi das unterstützt. Hab ich allerdings weggelassen, denn ansonsten könntest du ja auch direkt
type
TMyThread = class(TThread) private procedure First; procedure Second; protected ExecutionRoutine: TProcedure; public procedure Execute; override; end; procedure TMyThread.Execute; override; begin while not Terminated do begin ExecutionRoutine; end; end; procedure TMyThread.First; begin //Falls (nicht B), unterbreche für zweite Routine if not B then begin ExecutionRoutine := Second; end; end; procedure TMyThread.Second; begin //Nach Beendigung wieder erste Routine ExecutionRoutine := First; end;
Delphi-Quellcode:
nehmen.
TThread.CreateAnonymousThread(...)
|
AW: 2 Threads starten/pausieren...
Das würde ich anders lösen. Ich würde das ganze in Pakete aufteilen, die abzuarbeiten sind. Die Größe der Pakete hängt davon ab wie lange es dauert diese zu bearbeiten. Auf die Weise kannst du schnell unterbrechen, auch wenn immer das komplette Paket bearbeitet wird.
Die einzelnen Pakete kannst du dann in eine Queue packen, der Thread holt sich dann jeweils immer das nächste Paket heraus. Die Queue muss natürlich threadsicher sein. Von außen kannst du nun einfach Einfluss auf die Abarbeitung der Pakete nehmen, z.B. indem du ein Paket an Platz eins setzt und so weiter. Auf die Weise kannst du einzelne Pakete priorisieren oder nach hinten schieben. |
AW: 2 Threads starten/pausieren...
Zitat:
|
AW: 2 Threads starten/pausieren...
Danke für die Vorschläge.
Noch etwas schwer für mich... mit first/second oder Queue-Dingsbumbs :) Meine eigene Überlegung mit dem Code den ich hier aufgeschrieben habe, ist auch nicht so einfach. Da muss man aufpassen denn sobald die Variablen außerhalb des Threads gesetzt werden, werden diese ja auch im Thread übernommen. Da kommt es dann drauf an, wo der Thread gerade die Zeile verarbeitet. Dennoch habe ich damit weiter probiert und habe es jetzt so:
Code:
Außerhalb des Thread setzte ich dann nach Benuteraktion:
iV := 0; // Originale Start-Zahl (immer 0)
iB := Length(FDR)-1; // Originale End-Zahl (z.B. bis 5000) i := 0; // Ab 0 beginnen userpos := false; // False = normaler Durchgang repeat daten[i]... i := i + 1; if ucheck = true then begin iv := i; i := uiv; ib := uib; if ib > Length(FDR)-1 then ib := Length(FDR)-1; ucheck := false; userpos := true; end; if userpos = true then begin if i>= ib then begin i := iv; ib := Length(FDR)-1; userpos := false; end; end; until i >= ib;
Code:
So geht es einigermaßen aber der Thread wird nicht beendet, wenn der Benutzer ständig Aktionen durchführt. Da muss ich noch rumbasteln. Aber so im Ganzen ist es wohl auch nicht optimal, wenn ich gerade drüber nachdenke.
Thread1.uiv := neue_start_zahl; // z.B. 200
Thread1.uib := neue_end_zahl; // z.B. 1000 Thread1.ucheck := true; Eigentlich soll es so sein: Der Thread soll halt ganz normal von 0-5000 (als Beispiel) die Schleife verarbeiten. Kommt der Benutzer und will (200-1000) dann sollen diese erster verarbeitet werden. Sind diese aber schon durch von der normalen Schleife 0-5000 dann müssen diese 200-1000 nicht verarbeitet werden. Der Benutzer kann jedenfalls zwischen dieser Zeit 200-1000 wieder eine neue Aktion durchführen und will 3000-4000 haben, diese dann aktuell verarbeitet werden sollen, solange diese nicht schon von der normalen Schleife verarbeitet wurden. Ich hoffe das ist verständlich :) Zum Glück sind meine "daten[i]..." Sachen (es ist ein Record) so aufgebaut.
Code:
Also es ist nicht so ganz schlimm, wenn die Schleifenzahl schon mal genutzt wurde.
nr := daten[i].nummer;
If nr = -1 Then Begin // ... Daten werden verarbeitet, gesetzt, gespeichert usw. // und das kostet etwas mehr zeit (mal 1 sek, mal 5 sek. je nach Inhalt, der immer anders sein kann) daten[i].nummer := zahl größer -1; End Ich kenne mich noch nicht so aus mit den Threads und was man alles so damit machen kann. Deswegen denke ich, es wäre der beste Weg, man hat einen Thread für die 0-5000 und ein weiteren Thread für die Benutzeraktion. Wenn der Benutzer-Thread ausgeführt werden soll, soll der 0-5000 Thread pause machen. Das könnte man ja so realisieren, dass die Schleifenzahl nicht weiter hochzählt. Ist der Benutzer-Thread fertig, dann wird die Schleifenzahl vom normalen 0-5000 Thread wieder weiter hochgezählt. Ob nun bei dem einem Thread oder beim anderen die daten[i]... schon verarbeitet wurden oder nicht, wäre ja nicht so schlimm, da ich dieses ja mit "nr := daten[i].nummer" auf "-1" prüfe. Ich habe schon gelesen, dass es nicht so optimal ist, wenn man Threads pausieren lässt und wieder startet. Wie gesagt, bei dem normalen 0-5000 Thread könnte ich ja zum Pausieren die Schleifenzahl nicht hochzählen lassen und ein Benutzer-Thread erstellen. Ist der Benutzer-Thread fertig, wird er einfach freigegeben. Wenn der normale 0-5000 Thread durch ist und beendet wurde, muss kein Benutzer-Thread mehr erstellt werden (da ja alles von den 0-5000 durch ist). So meine Überlegungen :) Michael |
AW: 2 Threads starten/pausieren...
Zitat:
Zitat:
Zitat:
|
AW: 2 Threads starten/pausieren...
Ich habe es jetzt so.
Den 0-5000 Thread erstelle in meinem OnCreate von der Form und setzte "Thread1.FreeOnTerminate := true;". "Thread1.Start" kommt an einer anderen Stelle zum Einsatz. Der geht nun ganz normal durch (mit Repeat/Until-Schleife) und wird nach beenden freigegeben. Der wird auch nicht pausiert. Aber das würde ich noch gerne einbauen. Daran versuche ich mich gleich. Der Benutzer-Thread erstelle ich so:
Code:
Solange der Thread1 (0-5000 Thread) läuft, kann ein UserThread erstellt werden.
if Thread1.Terminated = False Then
Begin UserThread.free; UserThread := TUserThread.Create(true); UserThread.FreeOnTerminate := false; UserThread.iv := UserStartZahl; UserThread.ib := UserEndZahl; UserThread.Start; End; UserThread.free brauche ich ja hier nicht prüfen oder? Wenn gerade ein UserThread läuft, wird durch "UserThread.free" dieser ja beendet und freigegeben. Wenn keiner läuft, geht "UserThread.free" ja auch, also es machts nichts, wenn keiner freigegeben werden muss. In der Execute von UserThread läuft dann eine For-Schleife (i := iv to ib) ab. So funktioniert es jedenfalls. Michael |
AW: 2 Threads starten/pausieren...
Zitat:
Wenn du den Thread später noch ansprechen möchtest, darfst du FreeOnTerminate nicht setzen. |
AW: 2 Threads starten/pausieren...
Zitat:
|
AW: 2 Threads starten/pausieren...
Zitat:
Denn der läuft im Hintergrund und blockiert damit den Haupt-Thread nicht mehr mit zeitaufwändigen Schleifen. Warum soll der nun pausiert werden wenn ein weiterer Thread gestartet wird? Ciao Stefan |
AW: 2 Threads starten/pausieren...
Warum so kompliziert.
Gib doch den einzelnen Datenelementen ein Flag "Hab ich schon" mit. Das wird gesetzt, sobald der Thread das Element verarbeitet hat. Außerdem gibst dem Thread mit, von wo bis wo er arbeiten soll (Standard 0 bis Ende). Dann lass den Thread laufen. Will der User nun plötzlich die Elemente 200-800 verarbeitet haben, gibst dem Thread einfach diese beiden Werte an. Der Thread verarbeitet den laufenden datensatz noch ab und kümmert sich dann um die vorgegebene Range. Durch das markieren mit "Hab ich schon" werden die ja auch kein zweites Mal angefaßt. Hat der Thread seine Range durch (also 200-800 abgearbeitet), gehts einfach wieder von vorn los mit 0-Ende. Durch das "Hab ich schon" arbeitet er dann von ganz allein die ganze Arie ab - selbst wenn der User plötzlich noch 1000-1600 sehen will. Alles in einem Thread, ohne kompliziertes Anhalten/Weiterlaufen/Synchronisieren. |
AW: 2 Threads starten/pausieren...
Zitat:
Es kommt natürlich immer darauf an was man genau braucht, das kann man von außen nicht genau sagen. |
AW: 2 Threads starten/pausieren...
Zitat:
Delphi-Quellcode:
setzen und starten, ohne den Thread zwischenzuspeichern? Das ginge, wenn überhaupt, nur noch entweder im
FreeOnTerminate
Delphi-Quellcode:
(was unsauber wäre) oder mit einem
Execute
Delphi-Quellcode:
(ebenso). Prinzipiell kannst du ihn ja in so vielen Variablen speichern, wie du lustig bist. Du darfst/solltest nur nach dem Starten der Ausführung nicht mehr auf mehr auf das Objekt ohne
with
Delphi-Quellcode:
zugreifen, sofern
try
Delphi-Quellcode:
gesetzt wurde. Davor jedoch kannst du so viel an dem Ding rumspielen wie es dir danach verlangt.
FreeOnTerminate
|
AW: 2 Threads starten/pausieren...
Sorry, aber TRY ...
Man darf NIEMALS wieder darauf zugreifen, sobald der Thread einmal gestartet wurde, egal wo. Ausnahme: Der Thread setzt diese Variable auf NIL, bevor er freigegeben wird. Zugriffe auf diese Variable aber auch nur noch abgesichert, also z.B. innerhalb einer CriticalSection. |
AW: 2 Threads starten/pausieren...
Zitat:
(Ich sehe generell keinen Sinn darin suspended zu erzeugen, egal was für ein Thread das ist. Entweder man braucht den Thread jetzt, dann kann er auch gleich loslegen oder man braucht ihn noch nicht, dann braucht man ihn auch noch nicht erzeugen.) Aber für genau diesen Zweck, ihn nach dem Erzeugen zu starten, kann man eine solche Variable natürlich benutzen. Aber eben danach nicht mehr. |
AW: 2 Threads starten/pausieren...
Hallo,
da sind wir wohl etwas durcheinander gekommen. Oder ich :) Also ich habe einen Thread der mit einer Repeat/Until-Schleife eine reihe von Zahlen durchgeht. Beispielsweise 0-5000. Den erstelle ich inaktiv in meinem Form-Create und mit „FreeOnTerminate = True“ (damit er sich selbst freigibt, wenn fertig). Inzwischen sind es zwei Threads davon, weil es auch zwei Datenhälften gibt, die wechselnd verarbeiten werden sollen. Passt so besser. Die beiden Threads nenne ich jetzt mal die Hauptthreads. Sie werden an einer passenden Stelle im Programm gestartet und kommen auch nur 1x zum starten. Nun greift beispielsweise der Benutzer während die beiden Hauptthreads laufen ein und es wird dann von einer Datenhälfte ein Paket benötigt. (z.b. 3000-4000). Damit alles schneller geht, sollen nun die beiden Hauptthreads Pause machen. Ich habe das nun so gelöst, dass es wiederum zwei weitere Threads gibt, die für die Aktion des Benutzers zuständig ist. Die nenne ich jetzt mal UserThreads. Die sind jedenfalls noch nicht erstellt, sondern werden erst erstellt, wenn der Benutzer die Aktion wählt, sprich das Paket von 3000-4000 als erstes geladen werden soll. Passiert so etwas, dann wird erst mal geprüft ob der Hauptthread für die richtige Datenhälfte überhaupt noch läuft. Ist er nämlich fertig, dann brauch man auch kein Userthread mehr. Diese Prüfung hatte ich mit einem „if .Terminated“ geprüft aber da war ich mir nicht so sicher, da diese Hauptthreads sich selber freigeben und ob man dann mit diesem „Termitated“ darauf noch zugreifen darf, ist mir nicht so ganz klar. Es geht zwar aber ich habe lieber eine globale Variable dafür eingesetzt. Die Benutzer-Aktion (1 von 2) schaut jedenfalls so aus:
Code:
Also beim ersten Mal ist ein „.free“ irgendwie nutzlos aber für weitere Aktionen ist hier dafür der beste Platz. Es macht wohl nichts aus, wenn ein Thread freigegeben werden soll, der gar nicht existiert. Bei einer 2. Aktion vom Benutzer wird er jedenfalls freigegeben und darauf wieder erstellt. So starte ich jedenfalls meine UserThreads. Hier bei den UserThreads habe ich kein „ FreeOnTerminate=True“ gewählt, da sie auch durch eine neue Aktion vom Benutzer abgebrochen werden sollen. Wegen „Priority“… ich muss da noch mal schauen ob das was bringt. In der Execute von UserThread1 (auch im anderen UserThread2) setze ich noch eine globale Varibale „ThreadsPause“ auf „True“, damit die beiden Hauptthreads nicht in der Repeat/Until Schleife weiter hoch zählen. Ist UserThread1 fertig, wird die Variable „ThreadsPause“ wieder auf „False“ gesetzt und die Hauptthreads laufen weiter bzw. sie laufen ja die ganze Zeit aber die Zählervariable dort, wird wieder weiter hochgezählt. Nach der letzten Benutzer-Aktion bleiben ja immer noch zwei UserThreads im Speicher. In meinem Form Destroy gebe ich dann noch die beiden UserThreads frei. So funktioniert das ganze eigentlich recht gut. Natürlich habe ich bei meinen Daten auch ein „hab ich schon“ also als „User[zählervariable].Nummer“ festgelegt. Die Nummer steht auf „-1“ und wenn die Daten verarbeitet wurden, wird diese auf einen anderen Wert (größer als -1) festgelegt. Also wenn der Benutzer ein Paket von 3000-4000 haben will und der Hauptthread ist hier noch nicht angekommen (wird später auf diesen Zahlenbereich kommen), dann ist dies nicht so schlimm. Ich prüfe ja auf den Wert „-1“ und nur dann kommen die rechenintensiven Befehle zum Einsatz. Das Ganze wollte ich ja nur mit einem Thread lösen aber die ganzen Variablen zu setzen (außerhalb) sind schwer im Thread zu prüfen, da der Thread ja läuft und immer wo anders ist, im Execute Bereich (mal Zeile 4, dann 7 und mal Zeile 13). Da macht es sich schwer, ein 0-5000 abzubrechen, dann 3000-4000 durchzugehen, wobei durch einen neuen Abbruch das ganze nur bis 3580 gegangen ist, weil der Benutzer ja schon wieder neue 200-1200 haben will. Dies mit Variablen so zu prüfen, dass sich nichts in die quere kommt, ist echt schwer. Bestimmt gibt es da Lösungen/Befehle etc. denn es gibt ja nicht nur „Execute“. Es sind ja auch meine ersten Erfahrungen mit Threads und so wie ich es jetzt habe, gefällt es mir natürlich dann… weils halt geht :)
If HThread1Finish = False Then
Begin UserThread1.free; UserThread1 := TUserThread1.Create(true); UserThread1.FreeOnTerminate := false; Userthread1.Priority := tpTimeCritical; UserThread1.iv := startzahl // z.B. 3000 UserThread1.ib := enddzahl // z.B. 4000 UserThread1.Start; End; Ich hoffe es ist nun verständlich wie ich so meine Threads einsetze und zu welchem Zweck. Ich fülle Daten Records mit Werten, die dann zur Ansicht kommen. 2-3 Befehle (um mein Daten Record zu füllen) kosten dann etwas Zeit und damit der Benutzer auf diese Ansicht nicht warten muss, brauche ich dann diese Threads… die sicherlich besser gelöst werden können aber ich halt noch kein Durchblick dazu habe. :) Michael |
AW: 2 Threads starten/pausieren...
Klingt kompliziert, habs jetzt nicht komplett nachvollzogen was du machst ...
Ich würde einfach zwei Threads über Events steuerbar machen, so ![]() Dann könntest du die Threads intern immer laufen lassen, und ja nach Guste über die Events stillegen oder freischalten. Dann braucht man kein "Terminate" (nur am Programende, und keine globalen Variablen. Rollo |
Alle Zeitangaben in WEZ +1. Es ist jetzt 07:16 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