Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   System.Threading: Kann man einen Task / Threadpool beenden? (https://www.delphipraxis.net/183497-system-threading-kann-man-einen-task-threadpool-beenden.html)

Der schöne Günther 14. Jan 2015 21:17

System.Threading: Kann man einen Task / Threadpool beenden?
 
Es ist im Endeffekt die gleiche wie eine meiner ersten Fragen hier:
http://www.delphipraxis.net/173890-w...gewaltsam.html
Nur diesmal auf
Delphi-Quellcode:
System.Threading
bezogen:

Kann ich einen Task, im worst case-Szenario, richtig abbrechen? Richtig tot machen?

Ein einfaches Cancel() setzt vielleicht im Task-Objekt ein Flag, aber terminiert wird der entsprechende Thread dadurch nicht:
Delphi-Quellcode:
procedure justTaskingThings();
var
   myTask: ITask;
begin
   myTask := TTask.Run(
      procedure
      begin
         sleep(5000);
         WriteLn('Task finished');
      end
   );

   sleep(1000);
   WriteLn('cancelling task');
   myTask.Cancel();
end;
Erst schläft er sich noch richtig aus, dann wird die Zeile
Delphi-Quellcode:
WriteLn('Task finished');
ganz normal ausgeführt.
Ich dachte dann, es könnte helfen, explizit einen eigenen TThreadPool dafür auf zu machen und diesen dann zu beenden:

Delphi-Quellcode:
procedure justThreadingThings();
var
   myTask: ITask;
   pool: TThreadPool;
begin
   pool := TThreadPool.Create();
   try

      myTask := TTask.Run(
         procedure
         begin
            while True do TThread.Yield();
         end,
         pool
      );

      myTask.Wait(100);
      WriteLn('cancelling task');
      myTask.Cancel();
   finally
      WriteLn('destroying pool');
      pool.Free();
   end;
   Writeln('pool is closed');
end;
Diese Anwendung wird nie beendet werden da der TThreadPool-Destruktor ewig hängt denn er wird auf den blockierenden Thread warten.


Ich finde keine Lösung. Gibt es überhaupt eine?

Sir Rufo 14. Jan 2015 23:26

AW: System.Threading: Kann man einen Task / Threadpool beenden?
 
Ja und Nein.
Ja, man kann einen Task beenden
Nein einen Sleep kann ich nicht unterbrechen.

Wie unterbreche ich einen Task:

Indem man den Status des aktuellen Tasks abfragt mit
Delphi-Quellcode:
TTask.CurrentTask.Status
Delphi-Quellcode:
procedure Foo;
begin
  TTask.Run(
      procedure
    begin
      // nur loslaufen, wenn kein Abbruch
      if not TTask.CurrentTask.Status = TTaskStatus.Canceled
      then
        begin

        end;
    end );

  TTask.Run(
    procedure
    var
      LFinished: Boolean;
    begin
      // Arbeiten bis Abbruch oder Fertig
      while not( TTask.CurrentTask.Status = TTaskStatus.Canceled ) or LFinished do
        begin

        end;
    end );
end;
Das sollte dann auch dein Problem lösen.

Falls es da immer noch Probleme gibt, dann schau dir mal die Fixes für die Threading-Unit an: Kuckst du auf SO (Hat mich ein paar graue Haare gekostet) ;)

Stevie 15. Jan 2015 00:10

AW: System.Threading: Kann man einen Task / Threadpool beenden?
 
Warum sie kein CancellationToken eingebaut haben, ist mir echt unverständlich...
Sonst könnt man ja so nützliche Sachen machen.

Ich würd mir wohl nen eigenes "abbrechbares" Sleep bauen, was über
Delphi-Quellcode:
TTask.CurrentTask.Status
checkt, obs noch weiterpennen soll.

Der schöne Günther 15. Jan 2015 19:35

AW: System.Threading: Kann man einen Task / Threadpool beenden?
 
Das mit dem Sleep war nur ein dummes Beispiel. Bei mir ist es ein WinAPI-Aufruf der unter Umständen auf ewig blockiert. Bislang habe ich ihn in einem neuen Thread ausgeführt und wenn er nach einer bestimmten Zeit nicht fertig war habe ich ihn geplättet.

Ich dachte ich könnte dafür vielleicht jetzt System.Threading statt einer Eigenlösung nehmen, aber anscheinend nicht :-(

Sir Rufo 15. Jan 2015 20:38

AW: System.Threading: Kann man einen Task / Threadpool beenden?
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1286626)
Das mit dem Sleep war nur ein dummes Beispiel. Bei mir ist es ein WinAPI-Aufruf der unter Umständen auf ewig blockiert. Bislang habe ich ihn in einem neuen Thread ausgeführt und wenn er nach einer bestimmten Zeit nicht fertig war habe ich ihn geplättet.

Ich dachte ich könnte dafür vielleicht jetzt System.Threading statt einer Eigenlösung nehmen, aber anscheinend nicht :-(

Ähm, ja, wenn der WinApi.Aufruf blockiert wird und nicht wiederkommt ... hast du dir gedacht, dass Emba da jetzt ein wenig Zauberpulver genommen und ein paar salbungsvolle Worte dabei gesprochen hat und dann wird das schon? :shock:

Nein, Zaubern gehört bei denen noch nicht dazu.

Und wenn ein WinApi-Aufruf blockiert, dann schaue ich auch bei der WinApi, wie man das lösen kann ;)

Der schöne Günther 15. Jan 2015 22:05

AW: System.Threading: Kann man einen Task / Threadpool beenden?
 
Meine Geschichte zu erzählen, warum dieser Aufruf sich aufhängen kann und warum ich damit umgehen können muss führt zu weit.

Ich wollte noch nicht einmal Zauberpulver sondern nur wissen, wie man einen Task hart abbrechen kann. Und wie man einen Threadpool schließt wenn ein andauernder Task da drin ist.

Sir Rufo 15. Jan 2015 22:27

AW: System.Threading: Kann man einen Task / Threadpool beenden?
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1286635)
Meine Geschichte zu erzählen, warum dieser Aufruf sich aufhängen kann und warum ich damit umgehen können muss führt zu weit.

Ich wollte noch nicht einmal Zauberpulver sondern nur wissen, wie man einen Task hart abbrechen kann. Und wie man einen Threadpool schließt wenn ein andauernder Task da drin ist.

Niemand hat nach der Geschichte gefragt ... du hast das quasi ungefragt aus freien Stücken ohne Zwang erzählt. Nein, ich will es auch nicht tiefer im Detail wissen. Keine Angst.

Aber wenn du einen Thread hart gegen die Wand fahren musst, dann wirst du mit einem Framework, was die Verwendung von Threads abstrahiert nie niemals nicht besser damit fertig, denn dieses harte gegen die Wand fahren ist nicht möglich (oder sehr schwer und bringt dann dieses Framework eher aus dem Trab).

Mir geht es nur darum, dass du verstehst, warum du da kein Hexenwerk erwarten kannst.

Der schöne Günther 15. Jan 2015 23:43

AW: System.Threading: Kann man einen Task / Threadpool beenden?
 
Alles klar. Abseits meiner spannenden Geschichte finde ich es trotzdem etwas doof, selbst ständig auf "Cancelled?" prüfen zu müssen, mir wäre lieber gewesen, der Pool (oder wer auch immer) killt den entsprechenden Thread einfach wenn man von außen sagt "Schluss jetzt"

Vielleicht habe ich auch einfach nur ein verdrehtes Weltbild.
Nicht auszudenken, was ich als Politiker anstellen würde...

Sir Rufo 16. Jan 2015 02:07

AW: System.Threading: Kann man einen Task / Threadpool beenden?
 
Ja so ein Task kann genauso automatisch abgebrochen werden wie jede andere Methode. :roll:

Du schreibst gerade in dem Task eine große Datei, dann würgst du das von aussen ab, die Datei ist kaputt und dann fängt wieder das Geschrei an. Du bist der Programmierer und nur du kannst entscheiden, wann es sicher ist, die Aufgabe abzubrechen.

Wenn du eine Aufgabe in einem Thread killen möchtest, dann kannst du
Delphi-Quellcode:
System.Threading
nicht verwenden. So einfach ist das. Schreib dir einen eigenen ThreadPool wo dieses harte Abwürgen funktioniert und dann sind alle glücklich - bis zu dem Tag, wo man etwas abgewürgt hat, was man besser nicht gemacht hätte - aber die Funktion ist ja da, dann benutzen wir die doch. :)

Dejan Vu 16. Jan 2015 07:46

AW: System.Threading: Kann man einen Task / Threadpool beenden?
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1286644)
Alles klar. Abseits meiner spannenden Geschichte finde ich es trotzdem etwas doof, selbst ständig auf "Cancelled?" prüfen zu müssen, mir wäre lieber gewesen, der Pool (oder wer auch immer) killt den entsprechenden Thread einfach wenn man von außen sagt "Schluss jetzt"[/SIZE]

Das wäre fein. Und kein Problem. Schreibe einfach einen Wrapper um die Win-API die
  • einen internen Konsistenzstatus pflegt
  • bei einem 'Schluss jetzt' kontrolliert abbricht
  • z.B. in einem eigenen Sleep auf das 'Schluß jetzt' reagiert (ebenso in einer IO-Operation etc.)

Der schöne Günther 16. Jan 2015 09:32

AW: System.Threading: Kann man einen Task / Threadpool beenden?
 
Zitat:

Zitat von Dejan Vu (Beitrag 1286653)
Das wäre fein. Und kein Problem. Schreibe einfach einen Wrapper um die Win-API die [...]

Habe ich auch schon längst :thumb:

Ich wollte ja nur wissen "Kann System.Threading den bestehenden Kram ersetzen?". Die Antwort war nein. :spin2:

Der schöne Günther 23. Mär 2016 11:42

AW: System.Threading: Kann man einen Task / Threadpool beenden?
 
Ich bin aber ehrlich gesagt immer noch nicht ganz zufrieden mit der Sache. Folgendes Beispiel, einmal ein Piep-Thread, und ein Piep-Task.

Delphi-Quellcode:
unit Unit3;

interface uses
   System.Classes,
   Vcl.Controls, Vcl.Forms, Vcl.StdCtrls;

type
  TForm3 = class(TForm)
   startTaskButton: TButton;
   startThreadButton: TButton;
   procedure startTaskButtonClick(Sender: TObject);
   procedure startThreadButtonClick(Sender: TObject);
  end;

var
  Form3: TForm3;

implementation uses System.Threading, System.SysUtils;

{$R *.dfm}

procedure proc();
const
   maxCount = 10;
var
   count: Integer;
begin
   count := 0;
   repeat
      Beep();
      Inc(count);
      Sleep(500);
   until (count = maxCount);
end;

procedure TForm3.startTaskButtonClick(Sender: TObject);
begin
   TTask.Run(proc);
end;

procedure TForm3.startThreadButtonClick(Sender: TObject);
begin
   TThread.CreateAnonymousThread(proc).Start();
end;

end.
Einmal Button drücken, dann Anwendung beenden. Der Thread wird durch
Delphi-Quellcode:
Halt
vernünftig vorzeitig beendet. Der Task arbeitet seine zehn Piepser immer ab.

Ausprobiert mit XE7. Ist das mit 10 Seattle immer noch so? Ich finde das ehrlich gesagt gruselig.

Sir Rufo 23. Mär 2016 11:49

AW: System.Threading: Kann man einen Task / Threadpool beenden?
 
Ist so ... Emba hat das Abbrechen von Tasks nicht so richtig auf dem Schirm gehabt.

Generell musst du für einen abbrechbaren Task den Abbruch aber auch selber behandeln. Der anonyme Thread wird einfach so beendet, was auch nicht die feine englische Art ist :stupid:

Der schöne Günther 23. Mär 2016 12:07

AW: System.Threading: Kann man einen Task / Threadpool beenden?
 
Natürlich sollte ich das. Mich ärgert halt ziemlich dass, sollte ich in einem Task einen dummen Fehler machen, er nie terminiert, man die Anwendung nur noch über den Taskmanager abschießen kann.

In System.Threading.pas steht sogar
Delphi-Quellcode:
// Check each thread to see if it is already marked for termination and/or if it is hung.
, aber geprüft wird da gar nichts. Einfach Destroy() auf allen Threads aufrufen, nur leider steht da

Delphi-Quellcode:
destructor TThreadPool.TBaseWorkerThread.Destroy;
begin
  if FRunningEvent <> nil then
    FRunningEvent.WaitFor(INFINITE); // This waits for the Execute to actually be called.

Im Endeffekt läuft es darauf hinaus, dass ich mich nicht traue System.Threading zu verwenden und weiterhin mir selbst immer Threads zusammenfummeln werde. Echt schade.

himitsu 23. Mär 2016 12:55

AW: System.Threading: Kann man einen Task / Threadpool beenden?
 
Destroy setzt auch Terminated auf True und genau das mußt du auch prüfen, in deinen Threads.
Bei Anonymen Threads kann man sich das Threadobjekt holen
Delphi-Quellcode:
if TThread.CurrentIrengwas.Terminated then raus;
.

Der schöne Günther 23. Mär 2016 13:00

AW: System.Threading: Kann man einen Task / Threadpool beenden?
 
Ja, in einem Task kann ich prüfen ob der "Current" Thread nicht eigentlich beendet werden soll

Delphi-Quellcode:
procedure proc();
begin
   repeat
      Beep();
      TTask.CurrentTask.CheckCanceled(); // Bringt nichts
      if TThread.Current.CheckTerminated() then Break; //Das hier bringt's
      Sleep(500);
   until False;
end;
Wie gesagt, mir geht es aber um den Fall dass im Code ein dummer Fehler ist dass er nie terminiert. Ein Deadlock, sonst was. Benutze ich die Task-Library, wird sich die Anwendung niemals beenden.

himitsu 23. Mär 2016 15:07

AW: System.Threading: Kann man einen Task / Threadpool beenden?
 
Threads/Tasks dürfen sich IMMER nur selber "definiert" beenden/anhalten.

Einfaches Beispiel: Der Thread macht grade beim Speichermanager eine Anfrage oder spielt mit Strings/Arrays rum ... wenn dabei der Thread extern beendet wird, kann das komplette Programm (alle Threads) blockieren.


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