Laden...

Anzahl von Threads in einem Service begrenzt?

Erstellt von mosspower vor 15 Jahren Letzter Beitrag vor 15 Jahren 2.157 Views
mosspower Themenstarter:in
456 Beiträge seit 2007
vor 15 Jahren
Anzahl von Threads in einem Service begrenzt?

Hallo "Kollegen",

ich habe neu in einer Firma angefangen, die noch in der "Steinzeit" lebt - das ist ja nichts neues. Es gibt ganz viele verschiedene Programme (meisst Access), die halt etwas machen. Totales Chaos eben. Nun portiere ich die Funktionalität der Programme nach C#. Ich habe nun angefangen einen einzigen Service zu machen, der wiederum für jede Routine einen eigenen Thread startet. Es können locker insgesamt 50 Routinen werden. Gleichzeitig im Dauereinsatz sind max. fünf Threads, die anderen Routinen starten entweder nach Request oder einmal am Tag. Später ist dann vorgesehen, eine kleine WCF-Anwendungs-GUI zu erstellen, um die Threads (also die Routinen) kontrollieren (ein/ausschalten) zu können und den Service als Ganzes pfegen zu können.
Gegenwärtig bin ich bei 11 Threads (Routinen). Nun stelle ich mir die Frage, ob es hier Grenzen gibt (was ich nicht glaube) oder aber ob der Aufbau so richtig ist. Wie würdet ihr das ganze angehen? Bin ich auf dem "richtigen Weg"? Ich möchte halt weg von zig kleinen Anwendungen (die dann auch noch net mal gekapselt auf der Datenbank "rumrödeln"), sondern eine Anwendung haben, die das Zentral macht. Was hat es denn mit den Threads so auf sich? Ist doch eigentlich egal, wieviele ich gleichzeitig laufen lasse (solange ein Thread.Sleep(1000) bei allen drinnen ist - jeweils nach einer Prozessroutine), sollte es doch keine Performanceprobleme geben, oder?

Wäre nett, wenn sich der ein oder andere hier mal zu Wort meldet, um mir Feedback zu geben. Danke schon mal im voraus hierfür.

Gruß

P.S. Alle Routinen benötigen (i.d.R.) keine GUI

2.187 Beiträge seit 2005
vor 15 Jahren

Hallo mosspower,

Wir haben auch Services für solche Aufgaben eingesetzt, aber diese haben einige Nachteile:

  • Umständliche Programmieren
  • Kompliziertes Debugging
  • Threading ist unbedingt nötig (und Threading ist kompliziert 😜 )

Wir haben dann von Diensten auf Konsolenanwendungen umgeschwenkt, die wir mit den Geplanten Tasks von Windows ausführen. Die Zeitsteuerung von den Geplanten Tasks ist erste Sahne und die Verwaltung geht super, da alles unter Systemsteuerung\Geplante Tasks aufgelistet ist. Und wenn man alle Assemblies im Gleichen Ornder ablegt (oder im GAC), kann man auch eine Saubere und eine einheitliche Artichtektur/ein einheitliches Design hinbekommen.

Gruß
Juy Juka

PS: Wie in dem folgenden Bild könnte es aussehen:

109 Beiträge seit 2008
vor 15 Jahren

Guten Abend!

Ich hab so eben nochmal nachgelesen und in meinem kleinem Buch steht, dass 25, bei Dual Core 50 Threads gleichzeitig bearbeitet werden von C#, die anderen folgen, wenn andere Threads ihre Prozedur abgeschlossen haben. Somit biste auf den richtigen Weg!

Lg Coke

3.971 Beiträge seit 2006
vor 15 Jahren

Hallo Mosspower,

  1. verwende bitte kein Thread.Sleep, sondern steig auf WaitHandle(AutoResetEvent, ManualResetEvent, Semaphore) oder Monitor.Wait/Pulse um.

  2. Wenn du die Anzahl der Threads begrenzt haben möchtest, dann steig auf ThreadPool um oder du entwickelst dir eine eigene entsprechend angepasste Implementierung.

  3. Die Threadanzahl ist beispielsweise allein schon vom Hauptspeicher her begrenzt. Jeder Thread bekommt standardmäßig 1 MB an Stackspeicher zu gewiesen. Bei ca. 3000 Threads wäre quasi auf x86 schluss (wobei BS, die Anwendung selbst, CLR usw. auch zusätzlich Speicher benötigen).

Hier noch ein sehr interessanter Artikel:
Risiken der Parallelität - Lösungen für 11 häufige Probleme in Multithreadcode

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

2.760 Beiträge seit 2006
vor 15 Jahren

Wir haben dann von Diensten auf Konsolenanwendungen umgeschwenkt

Haben wir auch... aber wir haben trozdem einen Dienst der quasi der "Masterservice" ist um unsere Prozesse ohne angemeldeten Benutzer starten zu können und die Konsolenanwendungen zur Laufzeit aus der Ferne zu steuern.
Sollte evtl. nicht unerwähnt bleiben da man sich sonst erhebliche Nachteile einhandelt.

2.187 Beiträge seit 2005
vor 15 Jahren

Hallo jaensen,

Geplante Tasks können auch ohne angemeldeten Benutzer gestartet werden.

Sollte evtl. nicht unerwähnt bleiben da man sich sonst erhebliche Nachteile einhandelt.

Welche Nachteile meinst du?

Gruß
Juy Juka

2.760 Beiträge seit 2006
vor 15 Jahren

Hmm... stimmt, ganz vergessen das das auch geht (zu früh).

Geplante Tasks können auch ohne angemeldeten Benutzer gestartet werden

Das wäre natürlich der erheblichste Nachteil gewesen 😉 Der Rest ist eher komfortabler Schnickschnack wie z.B. einen eigenen kleinen Client in den das Logging umgeleitet werden kann (Zentral vom ausführenden Dienst über Named Pipes entgegengenommen), der die Prozesse starten/stoppen/schedulen kann usw. Also ein Monitoring (z.B. Heartbeat) und Client für die Prozesse.

S
8.746 Beiträge seit 2005
vor 15 Jahren

Grenzen gibts es (siehe Handle bzw. Hauptspeicher) sind aber i.d.R. nicht relevant. 11 Threads sind Klingelkram. bei der Performance solltest du auf zwei Dinge achten:

  1. Bei kurz laufenden Threads (Request abarbeiten oder so) immer den Threadpool verwenden.
  2. Niemals (außer wenn es gar nicht anders geht) mit Polling arbeiten (durch Thread.Sleep gebremste Schleifen, die Verarbeitungszustände aktiv abfragen). Das kostet RICHTIG Performance, vor allem ist das völlig unnötig.
mosspower Themenstarter:in
456 Beiträge seit 2007
vor 15 Jahren

Hallo,

erst mal vielen Dank für eure Anregungen ...

Nachteile:

  • Umständliche Programmieren
  • Kompliziertes Debugging
  • Threading ist unbedingt nötig (und Threading ist kompliziert 😜 )

Ja, weil es umständlich (Debugging, Logging, Exceptionhandling ect.) ist, habe ich alles aus der Serviceklasse ausgelagert. Das bedeutet, dass die Start-Methode nur einen Thread startet, der sich dann um alles weitere kümmert. So kann ich lokal die Threads einzeln oder den ganzen Service komplett in einer Consolenanwendung testen und debuggen. Entwickelt wird natürlich auch mit der Consolenanwendung.

Mein Serviceklasse ist also sehr schlank .. sieht lediglich so aus:

namespace LoeweService {
  public partial class Service : ServiceBase {
    /// <summary>
    /// The service main class
    /// </summary>
    public Service() {
      InitializeComponent();
    }

    private LoeweServiceManager loeweServiceManager = null;

    /// <summary>
    /// Starting service point
    /// </summary>
    protected override void OnStart(String[] args) {
      this.loeweServiceManager = new LoeweServiceManager();
      this.loeweServiceManager.severe += this.Servere;
      this.loeweServiceManager.Run();
    }

    /// <summary>
    /// Ending service point
    /// </summary>
    protected override void OnStop() {
      this.loeweServiceManager.Stop();
    }

    /// <summary>
    /// Severe event from LoeweServiceManager object has been fired
    /// </summary>
    private void Servere(Exception ex) {
      base.Stop();
    }

    /// <summary>
    /// System is shutting down
    /// </summary>
    protected override void OnShutdown() {
      this.loeweServiceManager.Stop();
    }
  }
}
  1. verwende bitte kein Thread.Sleep, sondern steig auf WaitHandle(AutoResetEvent, ManualResetEvent, Semaphore) oder Monitor.Wait/Pulse um.

Also, ich bin ja (noch) nicht so tief in Sachen Threads drinnen gewesen, bzw. weiß nicht genau ob ich das hier brauche, bzw. wie ich das einsetzen soll. Ich erkläre einmal kurz, wie ich meine Anwendung aufgebaut habe.

Im Configfile gibt es einen Sectionhandler, hier können Services angemeldet werden, z.B. mit display-name="BlaService" class="BlaServiceClass" ...

Der Hauptservice Instanziert einen ThreadManager (hier LoeweServiceManager). Diese Klasse (Methode) läuft andauernd mit einem Thread.Sleep(1000) und wartet auf Ereignisse (Logging, Errors, Mailinganforderungen, Messages ect.). Bei der Initialisierung dieser Klasse werden die Services aus dem Configfile gelesen und in einem bestimmten Verzeichnis nach den Klassen (Attribut class) gesucht. Hier wird dann mittels Reflection eine Instanz von dieser Klasse gebildet und die jeweilige implementierte Run-Methode wird als Thread aufgerufen.

Sieht dann in einem Beispiel so aus ...

public class OrdersMatchingService : BaseThread, IThreadClass { ...


 public void Run() {
      
      try {
        while(!base.AbortRequest) {
          // Do some work
         Thread.Sleep(1000);
        } ...

Ich habe absolut keine Performanceprobleme und das gezeigte funktioniert auch ohne Probleme. Nur will ich ja dazulernen. Was würdet ihr an dieser "Architektur" verbessern? Es wird von den Threads nicht auf Resourcen (im Speicher) zugegriffen, die auch von anderen Threads benötigt werden - die Threads laufen alle völlig autark. Also weiss ich nicht, wie ich hier Monitor oder Waithandles einsetzen soll oder könnte, hier fehlt mir leider der Background oder aber ich brauche die für meine "Problematik" überhaupt nicht (zumindest verstehe ich das jetzt so). Danke schon mal im Voraus für Hilfe.

3.971 Beiträge seit 2006
vor 15 Jahren

und wartet auf Ereignisse

Wie sieht denn bei dir solch ein Ereigniss aus bzw. nenn einfach mal ein Beispiel aus der entsprechenden Klasse.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

G
497 Beiträge seit 2006
vor 15 Jahren

wenn die einzelnen Threads autark laufen, scheinen sie ja nicht viel miteinander zu tun zu haben. Was haben die dann in demselben Service verloren? Das ist eine Abhängigkeit, die einem nur Ärger machen kann. Jede Änderung an einem einzelnen Teilstück dieser später mal sehr umfassenden (vielleicht nicht mal im Code, aber in ihrer Tätigkeiten) Anwendung bedeutet, daß der Service gestoppt werden muss. Und man hat eine große, monolithische Anwendung, wo es gar nicht nötig wäre (und wo man auch gar keinen Vorteil davon hätte).

Keep it simple. Kleine Konsolenanwendungen, per Taskplaner aufgerufen, sind zwar nicht so elegant, aber auf lange Sicht viel einfacher zu warten und zu pflegen. Ich mach manche einfache Sachen (z. B. ein paar Backup-Skripte für MSSQL Express-Server) nicht mal in C# sondern in Perl, da kann ich schnell mal Anpassungen vornehmen.

Und wenn du alles in einer Anwendung halten willst: Such dir einen Scheduler (Quartz.net zum Beispiel, ist auch im Spring-Framework enthalten) und bau dir einen eigenen Taskplaner, der dann zum gegebenen Ereignis deine Verarbeitungsmethoden mit dem ThreadPool aufruft. So hast du nicht permanent einen Haufen Threads, die auf Arbeit warten.

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

und wartet auf Ereignisse
Wie sieht denn bei dir solch ein Ereigniss aus bzw. nenn einfach mal ein Beispiel aus der entsprechenden Klasse.

Sorry, ist ein bißchen später, ich hatte den Thread leider aus den Augen verloren.

Beispiele wären:

Ein bestimmtes Datum für Generierung von Rechnungen oder Erinnerungsmails werden verschickt für bevorstehende Prozesse. Files werden in Verzeichnissen gefunden, die dann Import werden, bzw. "aufgeräumt" werden. Tägliche Abgleiche über andere Datenbank usw.

Hört sich alles sehr unterschiedlich an, ist es ja auch, aber dies wird alles für einen Kunden im Rahmen des Warehousings durchgeführt, also gehören die thematisch schon zusammen.

Ich möchte halt einen Service und nicht zig andere Anwendungen (gegenwärtig noch teilweise in drei verschiedenen Programmiersprachen entwickelt). Das wird alles zusammengeführt.

@GarlandGreene,
wegen der Komplexität habe ich ja ein kleines "Services-In-Services"-Framework geschrieben, so dass man nur im Konfigfile den Klassennamen angeben braucht, mittels Reflection wird dann die Run-Methode aufgerufen und man kann gleich zum Coden loslegen, ohne sich um das ganze "Service-Gedöns" zu kümmern - auch können mehrere Coder so unabhängig Teilservices implementieren. Wenn einer natürlich Schrott baut, dann zieht das nicht den ganzen Service mit runter, sondern der Thread raucht halt ab und ja, bei Änderrungen muss der Service neu gestartet werden, das ist aber bei Anwendungen auch nicht anders, die müssen auch neu gestartet werden.

Gruß

3.971 Beiträge seit 2006
vor 15 Jahren

Du kannst auch ein eigenes PluginSystem in deinen Dienst einbauen. Jeder Anwendungsteil bekommt eine eigene abgeschottete AppDomain. Hat ein Anwendungsteil einen Schwerwiegenden Fehler bleibt nur dieser Teil stehen und nicht der ganze Service.

Zu den Threads:*Verwende ThreadPool *Vermeide Polling durch den Einsatz eines Timers (System.Threading.Timer, arbeitet intern auch mit ThreadPool), um wertvolle Ressourcen (Threads) nicht sinnlos zu blockieren, wenn noch andere Aufgaben warten. *Bei Threads die vom Start bis zum Beenden am leben sind, kannst du einen eigenen Thread erstellen.

Weiterhin, hast du beispielsweise mehrere Aufgaben, die an bestimmten Tagen zu bestimmten Uhrzeiten ausgeführt werden sollen, nutze die Windows geplanten Tasks. Do kannst du beispielsweise MiniTools schreiben, die die entsprechenden Funktionen mittels Remoting/WCF in deinem Dienst antriggern. Du ersparst dir eine lästige Eigenimplementation der geplanten Tasks.

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

2.760 Beiträge seit 2006
vor 15 Jahren

Du ersparst dir eine lästige Eigenimplementation der geplanten Tasks.

Oder man benutze das Quartz-Framework: http://quartznet.sourceforge.net/
[EDIT] Falscher link...

3.971 Beiträge seit 2006
vor 15 Jahren

@jaensen,
danke, das kannte ich noch nicht 😉

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...