Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Threads (tpIdle) blokieren Anwendung (https://www.delphipraxis.net/168854-threads-tpidle-blokieren-anwendung.html)

Manfred D2009 14. Jun 2012 13:50

Threads (tpIdle) blokieren Anwendung
 
Hi DP,

ich habe eine GUI-Anwendung mit mehreren Berechnungs-Threads. Die Threads haben für bestimmte Aufgaben synchronisierte Abschnitte. Diese Abschnitte sind notwendig und sehr kurz (wenige Millisekunden). Die Threads haben die Priorität tpIdle.

Wenn ich nun mehrere Threads starte, wird die Bedienung meiner Hauptanwendung sehr, sehr holprig, da die Threads durch das ständige Synchronisieren das Hauptprogramm blockieren (oft im Sekundenbereich).

Jetzt könnte ich weniger Threads starten oder in der Execute-Routine der Threads Sleep-Aufrufe platzieren, dann würde die Anwendung wieder rennen. Allerdings will ich, dass die Threads die größtmögliche Leistung haben, wenn in der Anwendung sonst nichts passiert.

Was kann ich machen, damit Idle-Prozesse auch wirklich "idle" sind?

CCRDude 14. Jun 2012 14:29

AW: Threads (tpIdle) blokieren Anwendung
 
Die Threads sind ja wirklich idle, nur Deine Synchronisation nicht. Synchronisation ist auch das, was bei Threads nahezu die größte Herausforderung ist.

Lösungsansätze hängen ab von der konkreten Problemstellung.

Ein Ansatz, den ich neulich verfolgt habe, ist, die Daten enifach in Blöcken zusammenzufassen. So hat mein Code etwa alle Dateien der Platte aufgelistet und jeweils nur Blöcke zu Hundert Dateinamen synchronisiert. Der verarbeitende Thread brauchte eh länger, so dass dies kein Problem darstellte.

Weiterhin ist die Frage, ob die Hauptanwendung tatsächlich synchronisieren muss. Wenn es um Weiterverarbeitung geht, wäre noch ein weiterer Thread doch auf gut genug. Oder ein threadsicheres Datenobjekt. Wenn es dann um Visualisierung geht, könnte das UI in einem angemessenen zeitlichen Abstand pollen statt für jedes neue Datum eine Synchronisierung reinzubekommen. Beispiel: wenn es darum geht, wieviele Dateien schon verarbeitet wurden, reicht es, etwa per TMultiReadExclusiveWriteSynchronizer (bzw. performanterer Varianten)-Objekt die Statistik zu befüllen und dort per Timer nur 10 mal die Sekunde abzufragen und auf die GUI zu klatschen.

Diese beiden sind aber auch nur zwei Ansätze von etlichen...

Sir Rufo 14. Jun 2012 14:32

AW: Threads (tpIdle) blokieren Anwendung
 
Da du deine Delphi Version nicht bekannt gibst ;) schau doch mal, ob du Delphi-Referenz durchsuchenTThread.Queue schon hast. Das ist Synchronisieren ohne Blockieren

Manfred D2009 14. Jun 2012 15:56

AW: Threads (tpIdle) blokieren Anwendung
 
Danke für die schnellen Antworten!

@Sir Rufo:
Ich habe D2009 :wink:
Das mit der Queue ist sicher ein interessanter Ansatz, geht aber leider bei mir nicht so einfach, da die Berechnungs-Threads, wenn sie erstmal dran sind, die Queue füllen und dann die GUI wieder nicht wie gewünscht zum Zug kommt. Außerdem wären die nötigen Umbaumaßnahmen sehr groß.

@CCRDude:
Zitat:

Die Threads sind ja wirklich idle, nur Deine Synchronisation nicht.
Mir ist klar, dass die Abarbeitung einer synchronisierten Methode im MainThread stattfindet. Aber müsste nicht eigentlich ein idle-Thread gar nicht erst zum Absetzen seiner Synchronize-Routine kommen, wenn das Hauptprogramm was tun will? Werden die idle-Threads vom Betriebssystem so "hochpriorisiert", dass diese ihre sync. Methoden absetzen können und so den MainThread ausbremsen?

Ich kann leider nicht so einfach auf die Synchronize-Geschichten verzichten, da ich Ausgaben machen muss und an manchen Stellen threadsichere Datenzugriffe brauche.

Gib es vielleicht eine Möglichkeit um festzustellen, ob vor/nach einer zu synchronisierende Routine der MainThread noch etwas Wichtiges zu tun hat. Wenn ja, könnte man die Threads ja kurz mal schlafen legen...

BUG 15. Jun 2012 01:13

AW: Threads (tpIdle) blokieren Anwendung
 
Zitat:

Zitat von Manfred D2009 (Beitrag 1170911)
da die Berechnungs-Threads, wenn sie erstmal dran sind, die Queue füllen und dann die GUI wieder nicht wie gewünscht zum Zug kommt.

Mhm, dass klingt so, als ob die synchronisierten Stellen sehr oft aufgerufen werden.

In dem Zusammenhang:
Zitat:

Zitat von Manfred D2009 (Beitrag 1170911)
und an manchen Stellen threadsichere Datenzugriffe brauche.

Vermutlich wärst du da mit CriticalSections oder anderen feingranularen Synchronisationsmitteln besser dran.


Zitat:

Zitat von Manfred D2009 (Beitrag 1170911)
Aber müsste nicht eigentlich ein idle-Thread gar nicht erst zum Absetzen seiner Synchronize-Routine kommen, wenn das Hauptprogramm was tun will?

Ich denke eher, dass das das Hauptprogramm gerade den synchronisierten Teil ausführt, wenn der Nutzer eigentlich was interaktives Machen will. Außerdem könnte es sein, dass Synchronisierungs-Nachrichten von der Anwendung/Windows gegenüber UI-Nachrichten bevorzugt werden :gruebel:

Auf einem Multicore-System könnten theoretisch auch Hauptthread und Idle-Thread gleichzeitig ausgeführt werden.

Manfred D2009 15. Jun 2012 07:15

AW: Threads (tpIdle) blokieren Anwendung
 
@BUG:
Du hast recht, dass ich sehr häufig kleine synchronisierte Abschnitte lostrete. CriticalSections helfen zwar, die sync. Abschnitte zu verkleinern, aber auch in die threadsicheren Abschnitte darf trotzdem nur ein Prozess rein.

Und das Hauptproblem ist wohl, wie du sagst, dass auf einem Multicore-Rechner die Idle-Threads ja idle sind und deshalb weitermachen, ihre sync. Aufrufe loszuschicken.

Optimal für mich wäre eine Variante, bei der ich meine Threads bremse (evtl. mittels Sleep), wenn der Main-Prozess arbeitet und umgekehrt...
Gibt´s weitere Alternativen?

sx2008 15. Jun 2012 07:36

AW: Threads (tpIdle) blokieren Anwendung
 
Hast du das EVA-Prinzip (Eingabe-Verarbeitung-Ausgabe) beachtet?
Ein Thread-Objekt bekommt alle seine Inputdaten als Kopie schon vor dem Start übergeben.
(z.B. über Properties der Threadklasse)
Soll ein Thread z.B. Daten per FTP herunterladen, dann bekommt er ein fertig initialisiertes IdFtpClient-Objekt übergeben.
Der Hauptthread gibt dem Thread von Aussen alles was er braucht; der Thread soll sich um nichts kümmern müssen.

Während der Thread läuft sollte die Synchronisierung mit dem Hauptthread auf ein Minimum beschränkt bleiben.
Nachdem der Thread fertig ist präsentiert er seine Ergebnisse über "Output"-Properties.
Der Hauptthread (benachrichtigt über das Event OnTerminate) holt sich die Daten ab und vereinigt sie ggf. mit den Daten der anderen Threads.

Natürlich kann diese Vorgehensweise nicht immer so eingehalten werden, weil die Aufgaben zu verschieden sind, aber es hat grosse Vorteile wenn man so vorgehen kann.

Manfred D2009 15. Jun 2012 08:12

AW: Threads (tpIdle) blokieren Anwendung
 
Zitat:

Hast du das EVA-Prinzip (Eingabe-Verarbeitung-Ausgabe) beachtet?...
...
Natürlich kann diese Vorgehensweise nicht immer so eingehalten werden, weil die Aufgaben zu verschieden sind, aber es hat grosse Vorteile wenn man so vorgehen kann.
Mir ist wie euch allen klar, das man auf diese sync. Abschnitte so gut es geht verzichten sollte und es wäre auch in meinem Sinn die Threads als BlackBox zu deklarieren und diese mit allem zu versorgen, was nötig ist (mache ich bereits zu einem größtmöglichen Teil). Allerdings muss Aufwand/Nutzen betrachtet werden und was bei der Threadentwicklung sehr schnell problematisch wird, wenn irgendwelche Bugs entstehen...

Das System läuft prinzipiell auch sehr gut. Nur, wie bereits erwähnt, gibt es Tuning-Probleme, wenn viele Threads laufen.

BUG 15. Jun 2012 09:05

AW: Threads (tpIdle) blokieren Anwendung
 
Zitat:

Zitat von Manfred D2009 (Beitrag 1170995)
Das System läuft prinzipiell auch sehr gut.

Wenn es sekundenlang hängt, ist nicht sehr gut :mrgreen:


Ich vermute mal, dass dein Hauptproblem ist, dass die Synchronisationsnachrichten gegenüber den "interaktiven" Nachrichten bevorzugt werden* und schlage folgendes vor:
  • Jeder Thread kann sich in eine eine threadsichere Queue eintragen, wenn der Thread etwas synchronisiertes machen möchte.
  • Der Thread schickt bei Wunsch, etwas synchronisiertes zu machen, eine niedrig-priore Nachricht an den Hauptthread.
  • Der Hauptthread kann, wenn er diese Nachricht erhält, die Queue abarbeiten und (mit CriticalSections gesicherte) Aktualisierungsprozeduren des Threads aufrufen.
Damit erreichst du, das du das Abarbeiten der "synchronisierten" Proceduren selbst kontrollierst und das nur passiert, wenn gerade Zeit ist.
Ob du das machen willst und dir damit das ganze Synchronisationszeug auf die Seele ziehst, ist natürliche deine Entscheidung :wink:

* Kann das jemand bestätigen oder verneinen? Man könnte das ja erstmal in einem Prototyp testen.

Manfred D2009 15. Jun 2012 10:16

AW: Threads (tpIdle) blokieren Anwendung
 
Zitat:

Wenn es sekundenlang hängt, ist nicht sehr gut
Wenn die Tuning-Probleme nicht wären, dann wär´s sehr, sehr gut!:thumb:

Dein Ansatz mit einer threadsicheren Queue gefällt mir auf den ersten Blick richtig gut, das werde ich auf alle Fälle weiterverfolgen und ausprobieren!

Zitat:

•Jeder Thread kann sich in eine eine threadsichere Queue eintragen, wenn der Thread etwas synchronisiertes machen möchte.
•Der Thread schickt bei Wunsch, etwas synchronisiertes zu machen, eine niedrig-priore Nachricht an den Hauptthread.
•Der Hauptthread kann, wenn er diese Nachricht erhält, die Queue abarbeiten und (mit CriticalSections gesicherte) Aktualisierungsprozeduren des Threads aufrufen.
Was ich noch nicht ganz verstanden habe: Wie meinst du das mit "Thread soll niedrig priorisierten Message an den Hauptthread schicken"? Gibt es eine Möglichkeit die Messages zu priorisieren (hab ich noch nie gemacht)?


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