Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi TTask & Synchronize Verständnisproblem (https://www.delphipraxis.net/192447-ttask-synchronize-verstaendnisproblem.html)

weisswe 19. Apr 2017 10:36

Delphi-Version: 10 Berlin

TTask & Synchronize Verständnisproblem
 
Hallo!

Habe ein Verständnisproblem bei TTask.
Wollte nur Variablen der "Hauptprocedur" an den Task beim Start übergeben.
Jedoch bekomme ich nicht die richtigen Werte (meist -> 1,2,3,4,6,6).
Ich verstehe ja, das die Tasks unabhängig/zeitversetzt gestartet werden - nur wie bekomme ich Variablen "synchronisiert" beim Start des Tasks in den Task.
Hoffe habe mich irgendwie verständlich ausgedrückt. :?

Delphi-Quellcode:
   SetLength(tasks, anz);
   for i := 0 to anz - 1 do
   begin
      fn := Files[i].Filename;
      s := fn + ' wird heruntergeladen...';
      line := Memo1.Lines.Add(s);
      Files[i].Zeilennummer := line;
      tasks[i] := TTask.Create (
         procedure ()
         var j: Integer;
             tfn, ss: String;
         begin
            TInterlocked.Exchange(j, i); // !!?? zu spät?
            TThread.Synchronize(nil,
            procedure
            begin
               tfn := fn; // !!?? zu spät?
            end);
            ok := DownloadFile(tfn);   // Herunterladen - zeitintensiv
            TThread.Synchronize(nil,
            procedure
            begin
               ss := tfn + ' wurde heruntergeladen.';
               Memo1.Lines.Add(IntToStr(j) + ' - ' + ss);
            end);
         end);
      tasks[i].Start;
   end;

Uwe Raabe 19. Apr 2017 10:54

AW: TTask & Synchronize Verständnisproblem
 
Innerhalb der anonymen Methode wird immer der gerade aktuelle Wert der Variablen i und fn verwendet. Da dieser zur Ausführung in der Task schon ein anderer sein kann als zur Erzeugung der Task, kommt es zu diesem Verhalten. Du kannst aber die anonyme Methode auch über eine Funktion erzeugen lassen, der du den i- und fn-Parameter mitgibst. Dabei erhalten die Parameter ihre Werte aus dem Hauptthread und stehen der Task exklusiv zur Verfügung (Code ungetestet).

Delphi-Quellcode:
function CreateMyTask(I: Integer; fn: string; HandleResult: TProc<string>): TProc;
begin
  result :=
    procedure ()
    var
      ok: Boolean;
    begin
      ok := DownloadFile(fn); // Herunterladen - zeitintensiv
      TThread.Synchronize(nil,
        procedure
        var
          ss: String;
        begin
          if ok then begin
            ss := fn + ' wurde heruntergeladen.';
          end
          else begin
            ss := fn + ' wurde nicht heruntergeladen.';
          end;
          HandleResult(IntToStr(i) + ' - ' + ss);
        end);
    end;
end;

...

   SetLength(tasks, anz);
   for i := 0 to anz - 1 do
   begin
      fn := Files[i].Filename;
      s := fn + ' wird heruntergeladen...';
      line := Memo1.Lines.Add(s);
      Files[i].Zeilennummer := line;
      tasks[i] := TTask.Create(CreateMyTask(i, fn,
        procedure (Arg: string)
        begin
          Memo1.Lines.Add(Arg);
        end));
      tasks[i].Start;
   end;

Der schöne Günther 19. Apr 2017 10:57

AW: TTask & Synchronize Verständnisproblem
 
Und spätestens hier sollte man anhand der Lesbarkeit sehen dass es besser wäre das Herunterladen und das Anzeigen in der Oberfläche zu trennen. Auf Dauer bleibt es bestimmt nicht bei einem einfachen
Delphi-Quellcode:
Memo1.Lines.Add(..)
...
:warn:

Uwe Raabe 19. Apr 2017 12:04

AW: TTask & Synchronize Verständnisproblem
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1368241)
Und spätestens hier sollte man anhand der Lesbarkeit sehen dass es besser wäre das Herunterladen und das Anzeigen in der Oberfläche zu trennen. Auf Dauer bleibt es bestimmt nicht bei einem einfachen
Delphi-Quellcode:
Memo1.Lines.Add(..)
...
:warn:

:thumb: Vielleicht dann besser gleich so?

Dazu kommt noch, daß das Synchronize eh die letzte Aktion der Task ist und somit auch durch ein Queue ersetzt werden kann.

Delphi-Quellcode:
function CreateMyTask(I: Integer; fn: string; HandleResult: TProc<Integer, string, Boolean>): TProc;
begin
  result :=
    procedure ()
    var
      ok: Boolean;
    begin
      ok := DownloadFile(fn); // Herunterladen - zeitintensiv
      TThread.Queue(nil,
        procedure
        begin
          HandleResult(i, fn, ok);
        end);
    end;
end;

function CreateResultHandler(Target: TStrings): TProc<Integer, string, Boolean>;
begin
  Result :=
    procedure (Idx: Integer; FileName: string; OK: Boolean)
    var
      ss: string;
    begin
      if OK then begin
        ss := FileName + ' wurde heruntergeladen.';
      end
      else begin
        ss := FileName + ' wurde nicht heruntergeladen.';
      end;
      Target.Add(ss);
    end;
end;

...

SetLength(tasks, anz);
for i := 0 to anz - 1 do
begin
  fn := Files[i].Filename;
  s := fn + ' wird heruntergeladen...';
  line := Memo1.Lines.Add(s);
  Files[i].Zeilennummer := line;
  tasks[i] := TTask.Create(CreateMyTask(i, fn, CreateResultHandler(Memo1.Lines)));
  tasks[i].Start;
end;

Mavarik 19. Apr 2017 13:06

AW: TTask & Synchronize Verständnisproblem
 
Oder eher nicht...

Ok, der ThreadPool managed nur so viele gleichzeitige Thread wie es für die CPU - Core Anzahl sinnvoll ist, aber ist das auch sinnvoll für einen download?

i.d.R. nicht...

Hier würde ich die Anzahl begrenzen.
Das geht nicht über den Threadpool.

Mavarik

Zacherl 19. Apr 2017 14:37

AW: TTask & Synchronize Verständnisproblem
 
Zitat:

Zitat von Mavarik (Beitrag 1368255)
aber ist das auch sinnvoll für einen download?

Threads die primär IO-Operationen ausführen sind tatsächlich immer recht kritisch zu sehen. Zumindest ist deren Verhalten im Hinblick auf Performance deutlich abweichend zu CPU-lastigen Threads. Muss man denke ich individuell betrachten.

Zitat:

Zitat von Mavarik (Beitrag 1368255)
Hier würde ich die Anzahl begrenzen.
Das geht nicht über den Threadpool.

Doch doch, man kann die maximale Threadanzahl einstellen, oder sich sogar einen custom ThreadPool erzeugen, der dann dediziert nur für eine spezielle Art von Tasks verwendet werden kann :)

Mavarik 19. Apr 2017 15:50

AW: TTask & Synchronize Verständnisproblem
 
Zitat:

Zitat von Zacherl (Beitrag 1368282)
Doch doch, man kann die maximale Threadanzahl einstellen, oder sich sogar einen custom ThreadPool erzeugen, der dann dediziert nur für eine spezielle Art von Tasks verwendet werden kann :)

Ja, aber Achtung:

Zitat:

Zitat von System.Threading.pas
Delphi-Quellcode:
function TThreadPool.SetMaxWorkerThreads(Value: Integer): Boolean;
begin
  Result := Value >= TThread.ProcessorCount;
  if Result then
    TInterlocked.Exchange(FMaxLimitWorkerThreadCount, Value);
end;

Mavarik

Zacherl 19. Apr 2017 21:03

AW: TTask & Synchronize Verständnisproblem
 
Zitat:

Zitat von Mavarik (Beitrag 1368298)
Ja, aber Achtung:

Zitat:

Zitat von System.Threading.pas
Delphi-Quellcode:
function TThreadPool.SetMaxWorkerThreads(Value: Integer): Boolean;
begin
  Result := Value >= TThread.ProcessorCount;
  if Result then
    TInterlocked.Exchange(FMaxLimitWorkerThreadCount, Value);
end;


Das ist ja echt eine blöde Limitierung :?

jaenicke 20. Apr 2017 07:27

AW: TTask & Synchronize Verständnisproblem
 
Warum sollte man denn das Maximum geringer setzen wollen als die Anzahl der CPU Kerne?

Wenn man weiß, dass eine Aktion z.B. die Festplatte sehr stark beansprucht, ist ein Threadpool eher nicht geeignet, weil man dann ja gar nicht viele Tasks parallel möchte.

himitsu 20. Apr 2017 07:48

AW: TTask & Synchronize Verständnisproblem
 
Zitat:

Zitat von jaenicke (Beitrag 1368339)
Warum sollte man denn das Maximum geringer setzen wollen als die Anzahl der CPU Kerne?

Weil man das System nicht komplett auslasten will?


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