![]() |
Ansatz für Task-Queue Sequenz
Hallo zusammen,
für die Separierung von Aufgaben in Taksk möchte ich anonyme Methoden benutzen, und diese dann mit kleinen Delays hintereinander ausführen. Ich möchte einfach eine leicht zu handhabende Funktion um innerhalb von Actions und Events eine Ausführung "Ohne externen Timer" zu starten, wenn man noch innerhalb der eigentlichen EventFunktion ist. Ein Beispiel wäre FireMonkey TabControl -> OnTabChange. Wenn in der OnTabChange ein weiterer TabChange ausgelöst werden soll wird das ignoriert, weil das nach Verlassen des EventFunktion wieder rückgesetzt wird. Also müsste ich EventFunktion erst beenden lassen, und dann von ausserhalb den TabChange machen den ich möchte. Ich habe einen ersten Versuch mit TTask und TTread.Synchronize gemacht, siehe hier:
Code:
// Procedure sequence of syncronized Tasks
// iDelay determines a delay before or after the Task, to separate external Events // -iDelay means Delay before each Task, already before first // +iDelay means Delay after each Task, the first is w/o delay procedure Task_Sequence_SyncAndDelayed(iDelay : Integer; const aProcs : TS4Proc_Delegate_Array); begin TTask.Create(procedure () var I : Integer; tsk : ITask; begin for I := 0 to Length(aProcs)-1 do begin tsk := TTask.Create(procedure () begin if iDelay < 0 then Sleep(Abs(iDelay)); // xx milliseconds TThread.Synchronize(nil, TThreadProcedure(aProcs[I]) ); //Interact with UI if iDelay > 0 then Sleep(Abs(iDelay)); // xx milliseconds end).Start; tsk.Wait(); end; // End loop end).Start; end; Aufgerufen werden soll sas dann wie folgt: Es können mehrere Aufgaben hintereinandergeschaltet werden, falls nötig.
Code:
Task_Sequence_SyncAndDelayed(-500,
[ // 1st Task procedure begin //Interact with UI FFrmNotebook.ClearHeader( True ); ChangeTabActionViewNotebook.ExecuteTarget(ChangeTabActionViewNotebook); end, // 2nd Task, separated by Delay procedure begin //Interact with UI ChangeTabActionViewProject.ExecuteTarget(ChangeTabActionViewProject); end ]); Das scheint so zu funktionieren, aber ich bin nicht sicher ob es auch grundsätzlich in Ordnung ist das so zu machen. Womöglich gibt es Probleme mit Delphi, Firemonkey, iOS, MAXOS, Android, etc. Vielleicht gibt es ja auch schon eine Task-Queue die durch Delays getrennt wird, welche ich nicht gefunden habe. Wäre schön eure Meinung dazu zu bekommen. Rollo |
AW: Ansatz für Task-Queue Sequenz
Falscher Ansatz - würde ich sagen -
Das ist ein klassischer Ansatz für Sir Rufo's Idleworker... Hole dir
Delphi-Quellcode:
Dann nimm eine TaskListe wie
TMessageManager.DefaultManager.SubscribeToMessage( TIdleMessage, HandleIdleMessage );
Delphi-Quellcode:
In die Queue geht es mit
FTasks: TQueue<TProc>;
Delphi-Quellcode:
und die Verarbeitung geht so:
procedure TIdleWorker.Execute( Action: TProc );
begin FTasks.Enqueue( Action ); end;
Delphi-Quellcode:
Ich habe "ALLE" Application.Processmessages aus meiner App entfernt...
procedure TIdleWorker.HandleIdleMessage( const Sender: TObject; const m: TMessage );
var LTask: TProc; begin if FTasks.Count > 0 then begin LTask := FTasks.Dequeue( ); LTask( ); end; end; Warum waren die da drin? Beispiel:
Delphi-Quellcode:
Weil Firemonkey sonst NIE die Click oder Select Animation abspielt.. (Farbe setzen usw.)
Procedure TForm1.Button1Click(Sender : TObject);
begin Application.Processmessages; MachewasLanges; end; Daher sieht der User keine Reaktion.. Daher mache ich meine Verarbeitung nur noch im Application.OnIdle -> der ruft die obere Message auf... Dann sieht es so aus:
Delphi-Quellcode:
Bedeutet die App ist sofort wieder im MainThread und kann die ganze UI aufbauen... Und wenn alles fertig ist meine Routine aufrufen...
Procedure TForm1.Button1Click(Sender : TObject);
begin TIdleWorker.default.Execute(Procedure begin MachewasLanges; end; end; So habe ich alles umgestellt... Mavarik |
AW: Ansatz für Task-Queue Sequenz
Hallo Mavarik,
dankesehr für die Vorschläge. Habe mir schon gedacht das es was in Richtung Enqueue geben müsste. Das mit Application.ProcessMessages sehe ich auch so, deshalb hoffe ich das Delay() entsprechend sauber in allen OS umgesetzt wird. Ich möchte aber eben noch ein Delay mit einbauen, um die Ausführungen zu Verzögern, wenn ich statt Delay einen Performancetimer oder ähnliches nehme mache ich eine Loop die auch viel Rechenzeit für nichts verwendet. Ist da ProcessMessages nicht das kleinere übel ? Oder gibt es eine DelayFunktion die ohne ProcessMessages auskommt ? EDIT: Übrigens Zitat:
Rollo |
AW: Ansatz für Task-Queue Sequenz
Man kann dem IdleWorker noch zwei Arrays verpassen:
Delphi-Quellcode:
FTasks : T??? // 0..n
EarliestStartArr : Array of Integer // 0..n (GetTickCount+gewünschtes Delay) WaitAfterExecArr : Array of Integer // 0..n
Delphi-Quellcode:
procedure TIdleWorker.Execute( Action: TProc ; EarliestStart : Integer; WaitAfterExec : Integer);
begin FTasks.Enqueue( Action ); Setlength(EarliestStartArr,Length(EarliestStartArr )+1); EarliestStartArr[High(EarliestStartArr )] := EarliestStart; Setlength(WaitAfterExecArr ,Length(WaitAfterExecArr)+1); WaitAfterExecArr[High(WaitAfterExecArr)] := WaitAfterExec; end;
Delphi-Quellcode:
Nicht getestet, nur im Browser getippt.
procedure TIdleWorker.HandleIdleMessage( const Sender: TObject; const m: TMessage );
var LTask: TProc; begin if FTasks.Count > 0 then begin LTask := FTasks.Dequeue( ); while GetTickCount<EarliestStartArr[??] do sleep(1); LTask( ); sleep(WaitAfterExecArr[??]) // hier noch die Arrays anpassen: x[??] löschen und Längen dementsprechend anpassen end; end; |
AW: Ansatz für Task-Queue Sequenz
Du willst also etwas nach einer bestimmten Zeit im MainThread abarbeiten lassen.
Dann nimmt man sich eine Liste und fügt diese Methoden dort ein. Die Liste sortiert dann die Einträge nach den Delay-Werten (bzw. dem Zeitpunkt, ab wann diese Methode ausgeführt werden soll). Jedes Mal, wenn die Anwendung in den Idle-Zustand kommt, dann führt man die einfach die Methode aus, die jetzt an der Reihe ist. Der Vorteil dabei ist, dass du damit garantieren kannst, dass diese Methoden auch ausgeführt werden. Bei den Tasks geht das eben nicht (wenn man eine bestimmte Grenze überschreitet). Denn je nach CPU-Anzahl sind immer nur eine begrenzte Anzahl an Worker-Threads aktiv. Und wenn du nun dort einen Haufen Tasks einstellst, die mal eben 5 Minuten warten sollen und dann einen Task, der 10 Sekunden warten soll, dann kann es dir u.U. sogar passieren, dass dein 10 Sekunden Task erst nach ein paar Stunden aufgerufen wird! Die TPL funktioniert dort gut, wo ich Tasks habe, die auch ihre echte Arbeit aufnehmen, wenn diese gestartet wurden und diese Arbeit auch ein Anfang und Ende hat. Langläufer oder Langschläfer haben in der TPL nichts zu suchen, denn dafür ist diese nicht gebaut. |
AW: Ansatz für Task-Queue Sequenz
@Photoner
So definitiv nicht, sondern eine Liste aus so einem kleinen Record:
Delphi-Quellcode:
Eine Queue ist hier nicht mehr möglich, da man die Verarbeitung anhand der Startzeiten steuern möchte.
TWaitMethod = record
Proc : TProc; ExecuteAfter : TDateTime; end; |
AW: Ansatz für Task-Queue Sequenz
@Sir Rufo
Stimmt. Das würde blockieren. Wie wäre es damit: In HandleIdleMessage müsste man erst die EarliestStart Liste durchgehen und die erste Methothe die die Bedingung erfüllt wird ausgeführt. Erfüllt das keine, dann ists kein Problem mehr |
AW: Ansatz für Task-Queue Sequenz
Hallo Photoner,
danke für den Vorschlag. Aber da ist auch ein Sleep() drin. Wenn ich Mavarik richtig verstehe vermutet er darin ein Application.ProcessMessages, und das möchte er mit Recht verbannen. Da bleibt wohl nur mit GetTickCount zu selber zu zählen. Aber ist GetTickCount Platform-Spezifisch ? @Sir Rufo Ja, so ungefähr. Es sollte aber auch ein möglichst einfache Syntax sein. Also am Beispiel ChangeTabAction.ExecuteTarget(), da diese Zeile im Aufrufe Probleme macht möchte ich die etwas verzögern damit es nach dem Aufrufer ausgeführt wird. - ist nicht unbedingt etwas Langes - soll aber kein Syntax-Monster werden - Wenn möglich auch gleicht mehrere solcher Prags nacheinander schalten können, um z.B. Tabs mehrecht gleiten zu lassen Rollo |
AW: Ansatz für Task-Queue Sequenz
Du willst doch Dein Delay nur deswegen machen, damit das Tabchange usw. noch ausgeführt wird.. oder habe ich das falsch verstanden?
Da bei sowas die UITask beschäftigt ist, wird der onIdle sowieso nicht aufgerufen und warten ganz automatisch... Der Delay müsste ja processorabhängig sein. Mavarik |
AW: Ansatz für Task-Queue Sequenz
Sleep function
![]() kernel32.dll "Suspends the execution of the current thread until the time-out interval elapses." System.Sysutils gibt noch jede Menge Fktn. her. Stichworte:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:26 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