Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Wie Threads beenden und freigeben? (https://www.delphipraxis.net/191037-wie-threads-beenden-und-freigeben.html)

DCoderHH 1. Dez 2016 14:14


Wie Threads beenden und freigeben?
 
Ich habe eine eigene Thread-Klasse erstellt: TMyThread. Wenn der Benutzer auf einen Button klickt, wird eine Instanz dieser Klasse erzeugt und gestartet. Der Benutzer kann und soll das beliebig oft und schnell tuen können.

Immer wenn einen neue Instanz erzeugt/gestartet wird, sollen alle anderen Threads beendet und *freigegeben* werden. Die neue Instanz soll sofort gestartet werden, egal was die anderen Threads noch machen, das das Beenden zeitintensiv sein kann.

Beim Beenden des Programms sollen alle noch laufenden Thread beendet werden und das Programm erst schließen, wenn alle Thread fertig sind.

Ich habe dazu folgende Lösung. Problem dabei ist, das freigeben der laufenden Threads. Wenn ich das mit MyThread.FreeOnTerminate := true machen lasse, gibt's beim Programmende in TerminateAll() die Meldung "Thread-Fehler: Das Handle ist ungültig (X)' aufgetreten" Aber wie soll ohne FreeOnTerminate freigegeben werden?


Problem 2: Am Ende von TMyThread.Execute soll ein TNotify-Event ausgelößt werden, wenn der *zuletzt* gestartet Thread fertig ist. Evtl. hängen diese beiden Fragestellungen ja zusammen. Danke für die Hilfe!

Delphi-Quellcode:
type

  TMyThread = class(TThread)
  private
  public
    procedure Execute; override;
  end;

  TMyThreadList = class(TThreadList<TMyThread>)
  private
  public
    procedure TerminateAll;
    procedure AddAndStartThread(NewThread: TMyThread);
  end;

  TForm1 = class(TForm)
    btn1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure btn1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    procedure EvTerminate(Sender: TObject);

  public
    MyThreadList: TMyThreadList;
  end;

var
  Form1: TForm1;

implementation


{$R *.dfm}

procedure TMyThreadList.AddAndStartThread(NewThread: TMyThread);
var
  i: Integer;
  Items: TList<TMyThread>;
begin
  Items := LockList;
  try
    Items.Insert(0, NewThread);

    for i := Items.Count - 1 downto 1 do
      TMyThread(Items[i]).Terminate;

    NewThread.Start;
  finally
    UnlockList;
  end;
end;

procedure TMyThreadList.TerminateAll;
var
  i: Integer;
  Items: TList<TMyThread>;
begin
  Items := LockList;
  try
    for i := Items.Count - 1 downto 0 do
    begin
      TMyThread(Items[i]).Terminate;
      TMyThread(Items[i]).WaitFor;
    end;
  finally
    UnlockList;
  end;
end;

procedure TForm1.btn1Click(Sender: TObject);
var
  MyThread: TMyThread;
begin
  MyThread := TMyThread.Create(true);
  MyThread.FreeOnTerminate := false;
  MyThread.OnTerminate := EvTerminate;

  MyThreadList.AddAndStartThread(MyThread);
end;

procedure TForm1.EvTerminate(Sender: TObject);
begin
  MyThreadList.Remove(Sender as TMyThread);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  MyThreadList := TMyThreadList.Create;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  MyThreadList.TerminateAll;
end;

{ TMyThread }

procedure TMyThread.Execute;
var
  tc: Cardinal;
  a: Int64;
begin
  if not Terminated then
  begin
    //Das nur als Beispiel für Arbeit, die der Thread macht.
    while GetTickCount - tc > 10000 do
    begin
      if Terminated then
        break;
      inc(a);
    end;

    //Event hier, wenn das der letzte gestartete Thread ist.
  end;
end;

end.
Edit: Leider kann ich den Titel dieser Frage nicht ändern. Kann das mal bitte ein Admin erledigen? Z.B. in "Wie Threads beenden und freigeben?"

Der schöne Günther 1. Dez 2016 14:24

AW: Thread
 
Zitat:

Wenn ich das mit MyThread.FreeOnTerminate := true machen lasse
Dann ist
Delphi-Quellcode:
FreeOnTerminate
nichts für diesen Fall. Wenn ein Thread FreeOnTerminate hast solltest du nach dem Starten die Referenz nie wieder anfassen. Ab dann ist nicht mehr definiert ob die Thread-Instanz bereits freigegeben worden ist oder nicht.


Zitat:

Aber wie soll ohne FreeOnTerminate freigegeben werden?
Du hast mit deinem
Delphi-Quellcode:
EvTerminate
doch schon genau die richtige Stelle dafür! Entferne es nach wie vor aus deiner Liste und danach gibst du den TThread frei.

Zu Problem 2 würde ich deinem Thread die Logik dafür nicht mitgeben. Das
Delphi-Quellcode:
EvTerminate
ist doch auch hier eigentlich die perfekte Gelegenheit dafür!

DCoderHH 1. Dez 2016 14:32

AW: Thread
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1355151)

Zitat:

Aber wie soll ohne FreeOnTerminate freigegeben werden?
Du hast mit deinem
Delphi-Quellcode:
EvTerminate
doch schon genau die richtige Stelle dafür! Entferne es nach wie vor aus deiner Liste und danach gibst du den TThread frei.

Und wie? In OnTerminate darf kein Free aufgerufen werden.

Zitat:

Zitat von Der schöne Günther (Beitrag 1355151)
Zu Problem 2 würde ich deinem Thread die Logik dafür nicht mitgeben. Das
Delphi-Quellcode:
EvTerminate
ist doch auch hier eigentlich die perfekte Gelegenheit dafür!

Wie soll im OnTerminate der zuletzt gestartet Thread erkannt werden, wenn alle Threads gleichzeitig laufen und unterschiedlich lange Laufzeiten haben? Ein Thread kenn die anderen ja nicht und weiß in seinem OnTerminate nicht, was noch läuft und ob er der zuletzt gestartete ist.

bra 1. Dez 2016 14:49

AW: Thread
 
Du könntest nach dem Starten eines Threads alle anderen Threads aus der Threadliste löschen.

DCoderHH 1. Dez 2016 14:51

AW: Thread
 
Zitat:

Zitat von bra (Beitrag 1355160)
Du könntest nach dem Starten eines Threads alle anderen Threads aus der Threadliste löschen.

Welchen Zweck hätte das? Dann weiß ich beim Beenden des Programms nicht mehr, welche Threads noch laufen und kann sie dann nicht mehr beenden.

Klaus01 1. Dez 2016 14:57

AW: Thread
 
.. Du könntest das onTerminate Ereignis verwenden.
Damit könnte der Thread eine Nachricht absetzen wenn er beendet wird.
Die Nachricht könnte dein Haptprogramm veranlassen den Thread aus der Liste zu entfernen.

Edit.
Das machst Du doch schon - wo ist dann das Problem?

Grüße
Klaus

bra 1. Dez 2016 15:01

AW: Thread
 
Zitat:

Zitat von DCoderHH (Beitrag 1355161)
Zitat:

Zitat von bra (Beitrag 1355160)
Du könntest nach dem Starten eines Threads alle anderen Threads aus der Threadliste löschen.

Welchen Zweck hätte das? Dann weiß ich beim Beenden des Programms nicht mehr, welche Threads noch laufen und kann sie dann nicht mehr beenden.

Zitat:

Immer wenn einen neue Instanz erzeugt/gestartet wird, sollen alle anderen Threads beendet und *freigegeben* werden.
Irgendwie widersprichst du dir da gerade. Wenn ein Thread gestartet wird, geht er alle anderen Threads in der Liste durch, beendet sie und löscht sie aus der Liste. Was ist da jetzt das Problem?

DCoderHH 1. Dez 2016 15:11

AW: Thread
 
Zitat:

Zitat von bra (Beitrag 1355164)
Zitat:

Zitat von DCoderHH (Beitrag 1355161)
Zitat:

Zitat von bra (Beitrag 1355160)
Du könntest nach dem Starten eines Threads alle anderen Threads aus der Threadliste löschen.

Welchen Zweck hätte das? Dann weiß ich beim Beenden des Programms nicht mehr, welche Threads noch laufen und kann sie dann nicht mehr beenden.

Zitat:

Immer wenn einen neue Instanz erzeugt/gestartet wird, sollen alle anderen Threads beendet und *freigegeben* werden.
Irgendwie widersprichst du dir da gerade. Wenn ein Thread gestartet wird, geht er alle anderen Threads in der Liste durch, beendet sie und löscht sie aus der Liste. Was ist da jetzt das Problem?

Es ist kein Widerspruch:

Zitat:

Immer wenn einen neue Instanz erzeugt/gestartet wird, sollen alle anderen Threads beendet und *freigegeben* werden.
Dort wird und soll nicht darauf gewaret, dass die Threads wirklich beenden und freigegeben sind. Zu dem Zeiptunkt ist also nur der Wunsch an die Threads mitgeteilt worden, dass ich sie beenden möchten. Sie könnten dann aber noch laufen, während ich das Programm beenden möchte. Das Programm darf dann aber nicht beendet werden, weil die Threds noch laufen. Also muss ich hier die List haben um nachschauen zu können, ob die Threads noch laufen oder nicht.

DCoderHH 1. Dez 2016 15:15

AW: Thread
 
Zitat:

Zitat von Klaus01 (Beitrag 1355163)
.. Du könntest das onTerminate Ereignis verwenden.
Damit könnte der Thread eine Nachricht absetzen wenn er beendet wird.
Die Nachricht könnte dein Haptprogramm veranlassen den Thread aus der Liste zu entfernen.
Das machst Du doch schon - wo ist dann das Problem?

Ja ich entferne den Thread aus der Liste. Das ist aber nur die halbe Miete. Er soll auch freigegeben werden. Kann er aber nicht, da man free() in OnTerminate nicht aufrufen kann.

Sherlock 1. Dez 2016 15:16

AW: Thread
 
Threads haben zusätzlich zur Terminated noch die Finished Property. Die könntest Du abfragen, und jeden Thread, der Finished ist freigeben. Denn Terminated gibt eigentlich nur an, daß ein Thread den Terminate Befehl bekommen hat.

Und ein Thread.Free darfst Du beim entfernen aus der Liste auch machen.

Sherlock

Klaus01 1. Dez 2016 15:18

AW: Thread
 
Zitat:

Zitat von DCoderHH (Beitrag 1355169)
Zitat:

Zitat von Klaus01 (Beitrag 1355163)
.. Du könntest das onTerminate Ereignis verwenden.
Damit könnte der Thread eine Nachricht absetzen wenn er beendet wird.
Die Nachricht könnte dein Haptprogramm veranlassen den Thread aus der Liste zu entfernen.
Das machst Du doch schon - wo ist dann das Problem?

Ja ich entferne den Thread aus der Liste. Das ist aber nur die halbe Miete. Er soll auch freigegeben werden. Kann er aber nicht, da man free() in OnTerminate nicht aufrufen kann.

.. wenn die Liste leer ist - laufen keine Threads mehr,
denn du löscht den Thread aus der Liste wenn er sich beendigt hat.

Wie der Name schon sagt FreeOnTerminate wird der Thread freigegeben wenn er beendet ist.
Da musst Du nichts mehr freigeben.

Grüße
Klaus

mm1256 1. Dez 2016 15:34

AW: Thread
 
Zitat:

Zitat von DCoderHH (Beitrag 1355147)
Immer wenn einen neue Instanz erzeugt/gestartet wird, sollen alle anderen Threads beendet und *freigegeben* werden. Die neue Instanz soll sofort gestartet werden, egal was die anderen Threads noch machen, das das Beenden zeitintensiv sein kann.

Beim Beenden des Programms sollen alle noch laufenden Thread beendet werden und das Programm erst schließen, wenn alle Thread fertig sind.

Wenn ich das richtig sehe, läuft nie mehr als ein Thread, denn sobald einer gestartet wird, sollen die anderen ja beendet werden. Folglich kann zur gleichen Zeit immer nur ein Thread laufen. Es sei denn, der User schafft es schneller zu klicken (einen zweiten Thread zu starten) als vom System Zeit dafür benötigt wird, einen Thread zu starten. Irgendwie passt das alles nicht zusammen, oder ich bin zu kaputt um es zu kapieren :stupid:

Whookie 1. Dez 2016 16:38

AW: Thread
 
Liste der Anhänge anzeigen (Anzahl: 1)
Threads immer mit FreeOnTerminate = TRUE, damit hat man in Summe den geringsten Stress. Wenn das Ende des Threads von Bedeutung ist, einfach das OnTerminate - Event benutzen.

Das Warten beim Programmende kann man zwar machen, besser ist es ihmo den Thread so zu bauen, das er in Frieden sterben darf.

Die kleine Demo zeigt das Ende an und wartet (aber schön ist anders 8-))

Der schöne Günther 1. Dez 2016 16:53

AW: Thread
 
Ohne in dein Beispiel zu schauen, aber grade
Delphi-Quellcode:
FreeOnTerminate = True
und dann das Hauptformular zumachen ist gerade nicht in Frieden sterben lassen. Das ist eher von hinten anschleichen und in den Kopf schießen. :duck:

Oder meintest du dass man den Thread eher so bauen sollte dass er jederzeit einfach hart ausgeknipst werden darf?

a.def 1. Dez 2016 17:15

AW: Thread
 
Ist es eine Totsünde im FormCloseQuery bei CanClose True eine globale Variable auf TRUE zu setzen, welche der arbeitende Thread dann abfragt, seine Arbeit beendet und "sich in Frieden beendet"?
So würde ich es machen.

Klar, man kann auch auf Terminated prüfen. Aber ich persönlich würde es mit einer eigenen Variable machen und den Thread an sich gar nicht erst anfassen.


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