Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Dinge in meinen Thread queuen (https://www.delphipraxis.net/181268-dinge-meinen-thread-queuen.html)

Der schöne Günther 31. Jul 2014 14:02

Delphi-Version: XE5

Dinge in meinen Thread queuen
 
Ich habe bislang oft Sachen in den Hauptthread geschoben. Entweder mittels
Delphi-Quellcode:
TThread.Synchronize(nil, meineProzedur);
oder
Delphi-Quellcode:
TThread.Queue(nil, meineProzedur);
. So schön einfach.

Jetzt habe ich aber die Situation, dass ich etwas in einen anderen TThread queuen möchte. Was muss der Thread dafür tun? Er soll, wenn er die Zeit hat, die Sachen in seiner "To Do"-Liste abarbeiten.

Ich hätte jetzt etwas wie
Delphi-Quellcode:
ExecuteQueuedMethods
() erwartet dass ich in meiner Execute-Prozedur ausführen kann, aber das einzige was ich finden konnte war das genannte
Delphi-Quellcode:
RemoveQueuedEvents
. Das scheint die Queue aber nur zu leeren, nichts darin auszuführen. Die Doku gibt keine weiteren Ideen und aus dem undokumentierten Quelltext werde ich auch nicht schlau.

Uwe Raabe 31. Jul 2014 14:15

AW: Dinge in meinen Thread queuen
 
Der Thread führt einfach nur das Execute aus und beendet sich. Was auch immer darin gemacht wird, ist vollkommen deine Sache. Solch eine ToDo-Liste mit einem abarbeitenden WorkerThread musst du halt selbst programmieren - es gibt aber schon was fertiges: z.B: OmniThreadLibrary

himitsu 31. Jul 2014 14:16

AW: Dinge in meinen Thread queuen
 
Das "ExecuteQueuedMethods" nennt sich Classes.CheckSynchronize und das wird von der Messagebehandlung in Application aufgerufen.
Es lässt sich auch nir vom MainThread aus aufrufen und wirft ansonsten eine Exception.

RemoveQueuedEvents entfernt eine bestimmte, über TThread.Synchronize oder TThread.Queue hinzugefügte, Methode aus der Warteliste, bzw. alle von einem bestimmten Thread aus hinzugefügten Methoden, oder eine Kombination aus Beidem (Methode von bestimmten Thread).
(natürlich nur solange sie noch nicht ausgeführt wurden :stupid:)

Das was du willst, gibt es im Delphi noch nicht. (abgesehn von einigen Fremdkomponenten, die sich auf die Threadbehandlung spezialisiert haben)


Du kannst eine TThreadList oder TQueue<T> (mit zusätzliches CriticalSection) verwenden, um deine Aufgaben zu sammeln und rufst sie dann selber im Thread auf.

Der schöne Günther 31. Jul 2014 16:16

AW: Dinge in meinen Thread queuen
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1267167)
musst du halt selbst programmieren

Zitat:

Zitat von himitsu (Beitrag 1267169)
Das was du willst, gibt es im Delphi noch nicht.

Schade.

Überrascht mich, aber bei der geballten Kompetenz glaube ich das :wink:

DSP 10. Aug 2014 16:46

AW: Dinge in meinen Thread queuen
 
Gibts da nicht den http://www.delphipraxis.net/93835-wo...ntergrund.html von Alzi?

Der schöne Günther 12. Dez 2014 16:55

AW: Dinge in meinen Thread queuen
 
Hat sich mit dem neuen
Delphi-Quellcode:
System.Threading
unter XE7 etwas geändert oder geht es immer noch nicht?

Sir Rufo 12. Dez 2014 17:00

AW: Dinge in meinen Thread queuen
 
Wenn wir uns darauf einigen können, dass der Thread nur ein Ausführungskontext ist und in dem Kontext soll eine Aufgabe erledigt werden, dann ja, dafür ist dann z.B. der
Delphi-Quellcode:
ITask
(wie passend von der Bezeichnung) da.

Und innerhalb eines
Delphi-Quellcode:
ITask
kann man auch weitere Tasks erzeugen. Alle Tasks kommen in eine Queue und werden dann in irgendeinem Thread-Kontext abgearbeitet.

Der schöne Günther 12. Dez 2014 17:46

AW: Dinge in meinen Thread queuen
 
Aber grade das will ich ja nicht: Irgendein Thread.

Ich habe einen TThread, der hat irgendwann Daten berechnet/gesammelt. Mit denen soll in einem anderen Thread gearbeitet werden. Ich habe auf die Schnelle mal das hier hingeklatscht:

Delphi-Quellcode:
   TWorkThread = class abstract(TThread)
      private var
         workQueue: TThreadedQueue<TProc>;

      protected
         procedure Execute(); override;

      public
         constructor Create();
         destructor Destroy(); override;

         procedure pushWork(const proc: TProc); virtual;
   end;

{ TWorkThread }

constructor TWorkThread.Create();
begin
   inherited Create(True);
   workQueue := TThreadedQueue<TProc>.Create();
end;

destructor TWorkThread.Destroy();
begin
   workQueue.Free();
   inherited;
end;

procedure TWorkThread.Execute();
const
   sleepIntervalMs = 500;
var
   waitResult:   TWaitResult;
   workItem:   TProc;
begin
   Write('worker Thread ID is ');
   Write(TThread.CurrentThread.ThreadID);
   Write(sLineBreak);

   counter := 0;

   while not Terminated do begin
      waitResult := workQueue.PopItem(workItem);
      if (waitResult = TWaitResult.wrSignaled) then workItem();

      // Und jetzt den Kram, den der Thread eigentlich vorhatte...

      sleep(sleepIntervalMs);
   end;
end;

procedure TWorkThread.pushWork(const proc: TProc);
begin
   if not Assigned(proc) then Exit;
   workQueue.PushItem(proc);
end;
So einfach kann es doch sein, oder?

Sir Rufo 12. Dez 2014 17:52

AW: Dinge in meinen Thread queuen
 
Zuerst:

Du willst keinen Thread der sammelt, noch einen der berechnet!

Da gibt es eine Aufgabe des Sammelns und des Berechnens und diese beiden Aufgaben sollen in einem eigenen Thread-Kontext laufen.

Ist das ungefähr getroffen?

Sir Rufo 12. Dez 2014 18:12

AW: Dinge in meinen Thread queuen
 
Ganz einfach dargestellt sowas?
Delphi-Quellcode:
TTask.Create( 
  procedure
  var
    LData : TDataContainer;
  begin
    while not Aufhören do
    begin
      if DatenGefunden( LData ) then
        TTask.Create(
          procedure
          begin
            Verarbeite( LData );
          end );
    end;
  end );

Der schöne Günther 12. Dez 2014 18:14

AW: Dinge in meinen Thread queuen
 
Vielen Dank für die Hilfe bislang aber nein ;-)

Ganz abseits von konkreten Beispielen:

Ich möchte das, was man mit dem
Delphi-Quellcode:
TThread.Synchronize(..)
und
Delphi-Quellcode:
TThread.Queue(..)
dem Hauptthread zuwerfen kann, einem x-beliebigen Thread zuwerfen können.


Ein konkretes Beispiel:

Ich habe einen Polling-Thread der einen Zustandwechsel feststellt. Diesen Zustandswechsel von X auf Y soll ein anderer Thread mitbekommen und verarbeiten- Die Reaktion auf den Wechsel soll also nicht im Kontext des Polling-Threads erfolgen. Die Reaktion des Verarbeiters darf auch gerne erst kommen, wenn schon drei weitere Zustandwechsel stattgefunden haben, kein Problem.

Man sieht: Beide Threads existieren bereits und werden das auch die ganze Laufzeit über tun. Der Verarbeiter hat auch noch mehr zu tun als nur darauf mal zu reagieren. Aber die Reaktion muss halt in seinem Kontext geschehen. Genau wie der GUI-Hauptthread der ja üblicherweise auch andere Dinge macht als den Kram dem man ihm mit TThread.Synchronize(..) zuschiebt.

Zacherl 12. Dez 2014 18:21

AW: Dinge in meinen Thread queuen
 
Für so Dinge verwende ich meistens Events (MSDN-Library durchsuchenCreateEvent, MSDN-Library durchsuchenWaitForSingleObject). Die gibt es in den neueren Delphi Versionen glaube ich auch in einer Klasse gekapselt.

Thread 2 erstellt ein Event und wartet direkt im Execute auf signal. Thread 1 sinalisiert das Event, nachdem die Daten da sind. Thread 2 blockiert nun nicht mehr und bearbeitet die Daten. Wenn Thread 1 während der Bearbeitung weitere Daten hat, werden die ebenfalls an Thread 2 übergeben (hierfür sollte Thread 2 z.b. einen Stream bereitstellen. CriticalSections bei gemeinsamen Zugriff nicht vergessen!). Thread 2 arbeitet dann so lange, bis keine Daten mehr da sind, setzt dann das Event wieder zurück und fängt die Schleife von vorne an.

BTW: Wenn es dir konkret um eine Server Anwendung geht, dann such mal nach IO Completion Ports.

Der schöne Günther 12. Dez 2014 18:25

AW: Dinge in meinen Thread queuen
 
Grade das macht ja nur Sinn wenn der wartende Thread ("Thread 2") sonst keine Aufgabe hat.

Mavarik 12. Dez 2014 18:25

AW: Dinge in meinen Thread queuen
 
Zacherl war schneller!

Events...

Und eine Queue mit Locking...

Zacherl 12. Dez 2014 18:31

AW: Dinge in meinen Thread queuen
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1283310)
Grade das macht ja nur Sinn wenn der wartende Thread ("Thread 2") sonst keine Aufgabe hat.

Naja, wenn Thread 2 permanent irgendwas macht und nur zwischendurch Daten von Thread 1 verarbeiten soll, dann würde ich vermutlich einfach einen Boolean anlegen, der komplett von Thread 1 kontrolliert wird. Die Daten würde ich dann auch im Thread 1 zwischenlagern (Stream, Queue, etc.). Thread 1 schaut nun während seiner permanenten Aufgabe z.b. einmal pro Loop, ob der Boolean true ist und verarbeitet dann 1-n Pakete aus der Queue.

Edit: Ansonsten kannst du auch Events "peeken", indem du das Timeout von WaitForSingleObject auf 0 setzt und den entsprechenden Rückgabewert auswertest.

Sir Rufo 12. Dez 2014 18:37

AW: Dinge in meinen Thread queuen
 
Man sollte sich einmal die Frage stellen (und beantworten) warum gibt es für den GUI-Thread das Sync und Queue.

Evtl. weil einen die Standard Sync-Methoden (CriticalSection, Event, ...) dort nicht weiterbringen?
Aber die anderen (nicht-GUI-) Threads können mit denen sehr wohl umgehen.

Jetzt nochmal: Wozu ein EnqueueInThread?

Erstell eine Queue, sichere die mit einer CriticalSection und setze den Event. Dann hat man genau das.
Ob diese Queue dann von 1 oder n Threads abgefragt wird ist dann wumpe.

Der schöne Günther 12. Dez 2014 18:50

AW: Dinge in meinen Thread queuen
 
Ja, das führt in diesem Beispielfall mit dem Pollingthread auch zum Ziel. Aber allgemein bringt das noch kein "Wie führe ich X im Kontext von Thread Y aus?". Oder für meinen Kopf ist es heute schon zu spät.

Und warum kann ich bei TThread.Synchronize überhaupt ein anderes TThread-Objekt angeben? Die TThread-Instanzmethode CheckSynchronize springt mit Exception raus wenn sie nicht im GUI-Thread ausgeführt wird

//Edit
Zitat:

Zitat von Zacherl (Beitrag 1283312)
Edit: Ansonsten kannst du auch Events "peeken", indem du das Timeout von WaitForSingleObject auf 0 setzt und den entsprechenden Rückgabewert auswertest.

Oh. Gute Idee.

Sir Rufo 12. Dez 2014 19:11

AW: Dinge in meinen Thread queuen
 
Nehmen wir mal an, du bekommst Schafe und die Schafe sollen entweder geschoren, gefüttert oder gestreichelt werden.

Dann hast du da einen SchafStreichelThread, einen SchafScherThread und einen SchafFütterThread und denen gibts du dann die Schafe, die du bekommst.

Ich sage dieser Ansatz ist komplett falsch.

Erstelle dir einen SchafStreichler, SchafScherer und SchafFütterer. Dann bringst du das Schaf mit einem von den dreien zusammen und die Arbeit fängt dann an, wenn irgendein Thread das in die Hand nimmt.

Jetzt hat man die konkrete Aufgabe von dem Ausführungskontext getrennt und der Thread ist nur noch Kontext und die Daten mit dem Werkzeug sind nur noch Aufgabe.

Der schöne Günther 12. Dez 2014 19:15

AW: Dinge in meinen Thread queuen
 
Mein Phantasiebeispiel wäre dass der Pollingthread eine zu starke Temperaturschwankung innerhalb eines Zeitfensters registriert und das dann meldet.

Der andere Thread schaut "Gab es Schwankungen?". Wenn ja, holt er sich die zuletzt aufgetretenen und für ihn neuen Schwankungen. Er vergleicht das mit anderen Dingen, tut andere Dinge, kommuniziert mit anderen Geräten. Ich kann das nicht auf Objekte (Schafe) bei denen nur wichtig ist, dass bis irgendwann einmal bestimmte Aufgaben auf ihnen ausgeführt worden sind abbilden.

Man sieht: Der Pollingthread läuft mit einem kurzen Interval. Der Verarbeiter läuft in einem wesentlich langsameren Interval und hat eventuell relativ lange, blockierende Aufrufe drin. Es ist nicht wichtig, dass ich SOFORT auf diese Schwankungen reagiere, aber ich darf keine verpassen. Deshalb die Trennung in zwei.

Vielleicht ist es für mich auch schon zu spät :drunken:, ich grübel bis morgen das einmal durch. Vielleicht liege ich ja wirklich einfach daneben.

Zacherl 12. Dez 2014 19:27

AW: Dinge in meinen Thread queuen
 
Zitat:

Zitat von Der schöne Günther (Beitrag 1283319)
Man sieht: Der Pollingthread läuft mit einem kurzen Interval. Der Verarbeiter läuft in einem wesentlich langsameren Interval und hat eventuell relativ lange, blockierende Aufrufe drin. Es ist nicht wichtig, dass ich SOFORT auf diese Schwankungen reagiere, aber ich darf keine verpassen.

Klingt für mich, als ob mein Beispiel mit Thread 1 und Thread 2 dafür perfekt geeignet wäre :)

Sir Rufo 12. Dez 2014 22:54

AW: Dinge in meinen Thread queuen
 
Das ist genau das, was ich mit meinem TTask Beispiel gezeigt habe :wall:

Die Sammler-Aufgabe läuft dauernd und erzeugt neue Verarbeitungs-Aufgaben, wenn da etwas auftritt.


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