Laden...

Suche Alternative zu Thread.Sleep in bestimmtem Szenario

Erstellt von mosspower vor 15 Jahren Letzter Beitrag vor 15 Jahren 1.553 Views
mosspower Themenstarter:in
456 Beiträge seit 2007
vor 15 Jahren
Suche Alternative zu Thread.Sleep in bestimmtem Szenario

Hallo "Kollegen",

ich zermartere mir für meine Problemstellung, die ich gleich nennen werde, schon länger den Kopf und finde einfach keine Lösung (vielleicht brauche ich ja auch keine und kann beim Thread.Sleep bleiben, who knows).
Es ist ja verpönt, Thread.Sleep zu verwenden, erst recht, wenn ein Code nichts macht, z.B. nächster Start in zwei Stunden und dann immer nach einer Sekunde schaut ob Arbeit vorhanden ist, dann läuft der natürlich die ganze Zeit "leer".
Ich habe auch den Microsoft Webcast zum Thema Multithreading angesehen und mir im Web näheres zum Thema Multithreading angeeignet. Nur für meine Problematik finde ich keine Lösung.

Erst mal zum Hintergund. Ich habe gegenwärtig einen Windows Service laufen, der nach dem Start einen Thread startet, die sogenannte Controller-Klasse. In dieser werden voneinander unabhängige Threads als Teilservices, konfiguriert in meinem kleinen Windows-Service-Framework, gestartet. Somit können eigenständige Module in den Service integriert werden.

Mir ist bewußt, dass es Programmierer gibt, die hier einen anderen Lösungsansatz verfolgen würden, z.B. eigenständige Programme gesteuert mit Scheduler, wie z.B. Quartz. Ich will aber beim Service bleiben.

Hier ein paar Beipiele für sog. Teilservices im Service.

a) Täglicher Abgleich von Datenbanken
b) Monatliches Erstellen von PDFs
c) Durchgehendes Importieren von Files
d) Informationsbeschaffung aus dem Internet von verschiedenen Seiten (stündlich)

Die Aufgaben hören sich natürlich Unterschiedlich an, gehören aber alle zum gleichen Projekt.

Im Taskmanager laufen gegenwärtig so um die 35 Threads. Die CPU-Auslastung wechselt beim Server, nennen wir die Kiste "Serverchen mit 3.5 GHz", von 0 auf 1 Prozent im Sekundentakt und wieder auf 0. Also keinerlei Performance-Probleme.

Nur wie kann ich das, technisch gesehen, richtig programmieren?

Alle Teilservices laufen in einer Endlosschleife, Endbedingung ist, wenn Flag gesetzt wird, z.B. beim Runterfahren des Services und natürlich am Ende der Schleife mit Thread.Sleep im Sekundentakt.

Wie gesagt, ich habe keinerlei Performanceprobleme, nur ein schlechtes Gewissen, weil das ja verpönt ist. Vielleicht kann mir ja doch einer den richtigen Hinweis geben, wie ich das umgehen kann.

Gruß und Danke schon einmal für etwaige Antworten im Voraus.

P.S. Timer sind imo nicht die Lösung, da Threads in Kontext mit meinem kleinen Windows-Service-Framework flexibler sind und Timer ja auch letztendlich Threads sind, die imo doch auch irgendwo eine Art Thread.Sleep implementiert haben müssen.

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo mosspower,

Timer sind imo die Lösung. 😃

Eigenständige Programme brauchst du nicht unbedingt, wenn dir ein Dienst lieber ist, aber einen zentralen Scheduler (innerhalb des Dienstes) würde ich schon empfehlen. Und diesen würde ich über einen Timer realisieren. Der Scheduler sollte eine Liste der anstehenden Aufgaben und deren Terminen haben. Dann kann er das Timer-Intervall immer so einstellen, dass der Timer tickt, wenn die jeweils nächste Aktion ansteht. Die einzelnen Aufgaben kannst du dann auf unterschiedliche Threads verteilen, z.B. indem jeder Worker-Thread seine eigene Queue hat, in die der Scheduler den Arbeitsauftrag einstellt. Siehe Applikation mit Warteschlange

herbivore

1.361 Beiträge seit 2007
vor 15 Jahren

Hi,

ich finde auch, dass Timer sinnvoll wären.
Wie wärs mit derSystem.Timers.Timer-Klasse?

Du musst ja noch nicht mal explizit für deine Teilservices neue Threads erzeugen. Im Haupt-Thread legst du einfach nach Wunsch neue Timer an (oder hängst neue Aufträge an bereits bestehende Timer, falls mal auch was zeitgleich passieren soll).
So musst du dich auch gar nicht um das Threading kümmern. Denn die ausgelösten Methoden werden automatisch in Thread-Pool-Threads ausgeführt.

Ein zentraler Scheduler ist da vielleicht auch schon zu viel. Die Arbeitsaufträge werden bei dir doch eher vorkonfiguriert.
Anders wäre es, wenn asynchron stetig neue abzuarbeitende Anfragen kommen würden. Aber in deinem Szenario gehts ja mehr um sowas wie den TaskPlaner von Windows. (Den könnte man natürlich auch mit einzelnen Programmen nutzen =), Quartz ist hier vielleicht auch schon etwas übertrieben - aber gut, darüber wolltest du ja nicht diskutieren 😉)

beste Grüße
zommi

mosspower Themenstarter:in
456 Beiträge seit 2007
vor 15 Jahren

Hallo herbivore, hallo zommi,

ihr habt mich auf eine Idee gebracht, mit der ich in Zukunft ruhig schlafen kann 😁 ... das Thema Thread.Sleep() ging mir eigentlich schon lange auf den Senkel und ich hatte keine Lösung gefunden. Nun ist mir etwas eingefallen - würde mich auf einen Kommentar freuen, wenn die Umsetzung dann doch nicht so gut sein sollte.

  1. Ich starte den Service, dieser startet den Controller in einem Thread.

  2. In diesem Controller gibt es einen kleinen Scheduler, dieser läuft als Timer

  3. Es gibt eine Queue, in der alle Teilservices hinterlegt sind, für die die nächste Startzeit vorliegen.

  4. Das Interval im (Scheduler) Timer ist so eingestellt, dass immer bis zum nächsten Teilservicestart "getickt" wird, also erstes Item in der Queue - somit werden Leerlaufzeiten vermieden.

  5. Nach jedem Aufruf der Run-Methode eines Teilservices wird der Eintrag aus der Queue entfernt

  6. Der Aufruf der Run-Methode wird als asynchroner Methodenaufruf durchgeführt (BeginInvoke)

  7. Nach dem Ende der Teilservice-Methode (Run) wird in der Callback-Methode (EndInvoke) abgefragt, wann nächster Start ist. Die Zeit wird dann in die Queue neu "reinsortiert"

8 ) Bei allen Teilservices besteht die Möglichkeit auch von selbst durch ein Event (z.B. Bestimmtes File im Filesystem oder Eintrag in Datenbank) nächstes Startevent im Scheduler anzumelden

Was haltet ihr von dieser Vorgehensweise? Ist das jetzt "sauber" umgesetzt? Leerlaufzeiten, um die ging es mir ja hauptsächlich, gibt es demnach nicht mehr und es werden Threads aus dem Threadpool (asynchrone Methodenaufrufe) verwendet.

Vielen Dank nochmal für eure Hilfe!

P.S. Was mir auch noch sehr wichtig war ist die Tatsache, dass ein Programmierer durch unsachgemäße handhabe des kleinen Frameworks, den Services als ganzes nicht mehr "runterziehen" kann. Vorher zum Beispiel, wenn er Thread.Sleep vergessen hätte, dann wäre der komplette Service auf über 50 Prozent CPU-Auslastung "geeiert". Jetzt müsste er das ja nicht mehr machen und eine Endlosschleife a la while(!AbortRequest) ist jetzt auch nicht mehr nötig.

2.891 Beiträge seit 2004
vor 15 Jahren

Hallo mosspower,

solche Applikationen nennt man doch auch "Enterprise Job Scheduler", oder? Egal, jedenfalls habe ich auch schon einen programmiert.

Was haltet ihr von dieser Vorgehensweise? Ist das jetzt "sauber" umgesetzt? Ich habe es etwas anders gemacht. Ich habe zwei Threads und eine (blockierende) Warteschlage.
Der Consumerthread nimmt die Jobs aus der Warteschlage arbeitet sie (hintereinander) ab (=Run-Methode aufrufen, warten bis fertig, nächster Job). Warum führst du sie eigentlich asynchron aus?
Der Producerthread dagegen fragt jede Minute alle Jobs, ob sie denn jetzt ausgeführt werden wollen. Wenn ja, kommen die in die Warteschlange - um den Rest kümmert sich der Consumerthread. Das z.B. den Vorteil, dass ohne zusätzlichen Aufwand beliebig Jobs hinzugefügt und entfernt werden können. Bei dir müsste dann ja evtl. nochmal die Timerzeit geändert werden.

Gruß,
dN!3L

P.S.: Falls jemand Interesse an einem EJS, komplett mit Editor, remotefähig, Email-Benachrichtigungen, einfachen Datenbankzugriff, etc. hat, einfach melden 😃

mosspower Themenstarter:in
456 Beiträge seit 2007
vor 15 Jahren

Hallo dN!3L,

Warum führst du sie eigentlich asynchron aus?

Naja, es kann vorkommen, je nach Konfiguration, dass ein zeitkritischer Job zum Beispiel Arbeit für eine halbe Stunde hat (Abarbeitung pro Einheit in z.B. einer Sekunde) und dann ein anderer (ggf. auch zeitkritisch) vielleicht 5 Minuten, stehend vorher in der Queue, für die Abarbeitung benötigt, das würde bei einer synchronen Abarbeitung unter Umständen bedeuten, dass ein zeitkritischer Job fünf Minuten warten muss, bis er anfangen kann.

Bei dir müsste dann ja evtl. nochmal die Timerzeit geändert werden.

Ja, gegenwärtig habe ich vor, dass immer die Timerzeit nach den ersten Eintrag in der Queue, also auf die kürzeste Wartezeit, angepasst wird.

Gegenwärtig liest meiner in der App-Config alle Job-Deklarationen aus, das sieht dann so aus:


 <services>
      <service name="MMS orders matcher" class="OrdersMatchingService" prod.active="true" test.active="false" startOnHour="1" />
      <service name="MMS annulations matcher" class="AnnulationsMatchingService" prod.active="true" test.active="false" startOnHour="2" />
      <service name="MMS importer" class="MmsImporterService" prod.active="true" test.active="false" />
      <service name="MMS import sweeper" class="MmsImportSweeperService" prod.active="true" test.active="false" />
      <service name="Status data matcher" class="StatusMatchingService" prod.active="true" test.active="false" startOnHour="1" />
      <service name="UpToDate VNW notifier" class="VnwBuilderNotificationService" prod.active="true" test.active="false" />
      <service name="UpToDate VNW builder" class="VnwBuilderService" prod.active="true" test.active="false" />
      <service name="Legacy VNW builder" class="VnwBuilderServiceLegacy" prod.active="true" test.active="false" />
      <service name="Delivery mail importer" class="DeliveryMailImporterService" prod.active="true" test.active="false" />
      <service name="Delivery mail notifier" class="DeliveryNotificationService" prod.active="true" test.active="false" />
      <service name="TAT deployer" class="TatDeployerService" prod.active="true" test.active="false" />
      <service name="TOF deployer" class="TofDeployerService" prod.active="true" test.active="false" />
      <service name="UPS deployer" class="UpsDeployerService" prod.active="true" test.active="false" />
      <service name="UPS tracker" class="UpsTrackingReaderService" prod.active="false" test.active="true" />
    </services>

die werden dann mittels Reflektion gestartet. Unterstützt wird Mailing (Job-Reporting, Extern und Service-Mails, Benachrichtigunen), Logging (jetzt log4net), Auto-Exception-Handling bei Datenbank- und Netzwerkausfall, Message-Exchange, Handling für Konfigurationen, Auto-Serialisierung (und zurücksetzen beim Servicestart) von Job-Variablen

Remotingfähig werde ich den auch noch machen. Datenbank geht über meine NHibernate-Manger-Klasse

2.891 Beiträge seit 2004
vor 15 Jahren

Hallo mosspower,

das würde bei einer synchronen Abarbeitung unter Umständen bedeuten, dass ein zeitkritischer Job fünf Minuten warten muss, bis er anfangen kann. Also werden alle Jobs parallel ausgeführt? Bzw. gibt es auch die Möglichkeit, (bestimmte) Jobs sequentiell ablaufen zu lassen?

Gruß,
dN!3L

mosspower Themenstarter:in
456 Beiträge seit 2007
vor 15 Jahren

Hallo dN!3L,

Gegenwärtig ist es so, dass alle asynchron laufen - das werde ich auch in das neue Framework so einbauen. Ich habe es bisher synchrones Ausführen nicht gebraucht, falls, dann kümmern sich die Jobs darum, bzw. laufen eben nur, wenn bestimmtes Ergebnis / Ereignis vorhanden ist / stattfand.

Könnte man aber locker mit einbauen, müsste man halt zwei Scheduler (asynchron / synchron) laufen lassen.

Trotzdem gute Idee für Erweiterungen, danke!

Gruß

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo mosspower,

Ist das jetzt "sauber" umgesetzt?

im Großen und Ganzen ja. Allerdings bin ich persönlich kein Freund von Delegate.BeginInvoke. Daher war ja auch mein Vorschlag oben, blockierende Queues zu verwenden, um die Jobs zu verteilen. Dabei ist der Unterschied zu dN!3Ls Vorschlag, dass er nur einen Consumer-Thread verwenden. Mit mehreren Consumer-Threads kann man steuern, was man synchron und was man asynchron zueinander ausführen will.

herbivore