Delphi-PRAXiS
Seite 1 von 2  1 2   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   2 Threads starten/pausieren... (https://www.delphipraxis.net/196762-2-threads-starten-pausieren.html)

MicMic 17. Jun 2018 20:15

2 Threads starten/pausieren...
 
Hallo,

ich habe derzeit einen Thread den ich folgendermaßen aufrufe.
Code:
Thread1 := TSelfThread.Create(True);
Thread1.FreeOnTerminate := True;
An einer späteren Stelle mache ich dann Thread1.Resume.
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

jaenicke 17. Jun 2018 20:46

AW: 2 Threads starten/pausieren...
 
Zitat:

Zitat von MicMic (Beitrag 1405013)
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.

Die Kommunikation könnte z.B. per TEvent passieren, aber ich frage mich warum der erste Thread nicht auch diese Aufgabe übernimmt, wenn er seine Aufgabe derweil ohnehin nicht fortsetzen soll. Wer startet denn den zweiten Thread? Der erste Thread?

MicMic 17. Jun 2018 21:09

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

MicMic 17. Jun 2018 21:34

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:
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
Kann ich die Variablen "von", "bis" und "check" einfach setzen?

So vielleicht:
Code:
type
 TThread1 = class(TThread)
 private
 von,bis : integer;
 check : boolean;
 public
 procedure Execute; override;
end;
Und im Hauptprogramm, während der Thread läuft dann irgendwo wenn gewünscht einfach...
Code:
 Thread1.von := 50;
 Thread1.bis := 100;
 Thread1.check := true;
Michael

Dennis07 17. Jun 2018 22:18

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:
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;
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
Delphi-Quellcode:
TThread.CreateAnonymousThread(...)
nehmen.

jaenicke 17. Jun 2018 22:23

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.

Dennis07 18. Jun 2018 04:00

AW: 2 Threads starten/pausieren...
 
Zitat:

Zitat von jaenicke (Beitrag 1405024)
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.

Jo, so ließe sich das natürlich auch lösen. Wenngleich sich so eine Lösung wohl eher bei komplexeren Szenarien wirklich auszahlen würde. Bei zwei Paketen braucht man noch keine Queue, finde ich.

MicMic 18. Jun 2018 10:25

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:
 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;
Außerhalb des Thread setzte ich dann nach Benuteraktion:
Code:
 Thread1.uiv := neue_start_zahl; // z.B. 200
 Thread1.uib := neue_end_zahl;  // z.B. 1000
 Thread1.ucheck := true;
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.

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:
 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
Also es ist nicht so ganz schlimm, wenn die Schleifenzahl schon mal genutzt wurde.
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

Schokohase 18. Jun 2018 11:31

AW: 2 Threads starten/pausieren...
 
Zitat:

Zitat von MicMic (Beitrag 1405049)
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.

Wenn du das sagst - was immer du auch damit meinst
Zitat:

Zitat von MicMic (Beitrag 1405049)
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,

Meister Joda?
Zitat:

Zitat von MicMic (Beitrag 1405049)
solange diese nicht schon von der normalen Schleife verarbeitet wurden. Ich hoffe das ist verständlich :)

Nein.

MicMic 18. Jun 2018 11:47

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:
 if Thread1.Terminated = False Then
 Begin
  UserThread.free;
  UserThread := TUserThread.Create(true);
  UserThread.FreeOnTerminate := false;
  UserThread.iv := UserStartZahl;
  UserThread.ib := UserEndZahl;
  UserThread.Start;
 End;
Solange der Thread1 (0-5000 Thread) läuft, kann ein UserThread erstellt werden.
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


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:19 Uhr.
Seite 1 von 2  1 2   

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