Laden...

Gekapselte Threadklassen

Erstellt von mosspower vor 15 Jahren Letzter Beitrag vor 15 Jahren 2.177 Views
mosspower Themenstarter:in
456 Beiträge seit 2007
vor 15 Jahren
Gekapselte Threadklassen

[EDIT=herbivore]Dieser Thread war als Snippet gedacht, aber weiter unten zeigt sich, dass es einfacher geht, weshalb man das Snippet nicht benötigt. Deshalb verschoben nach Basistechnologien.[/EDIT]

Beschreibung:

Hier eine Utility-Klasse, mit der es möglich ist (wie in z.B. Java) Threads in Klassen zu kapseln.


using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Reflection;

namespace Util.ThreadClass {
  /// <summary>
  /// Utility class for handling thread class issues
  /// </summary>
  public class ThreadClassUtil {
    /// <summary>
    /// Gets a Thread object for the passed object
    /// </summary>
    /// <param name="threadClassInstance">The Thread object, must implement
    /// <see cref="ThreadClass.IThreadClass"/></param>
    /// <returns>The Thread object</returns>
    /// <exception cref="Exception">All possible reflection exceptions</exception>
    /// <exception cref="ArgumentException">If instance is passed null</exception>
    public static Thread GetThread(Object threadClassInstance) {
      Thread thread = null;
        
      try {
        // Check null
        if(threadClassInstance == null) {
          throw new ArgumentException("Parameter threadClassInstance was passed null");
        }

        // Check thread interface
        if(threadClassInstance.GetType().GetInterface(typeof(IThreadClass).FullName) == null) {
          throw new ArgumentException(String.Format("Type '{0}' does not implement mandatory interface '{1}'", 
            new Object[] { threadClassInstance.GetType().FullName, typeof(IThreadClass).FullName }));
        }

        MethodInfo methodInfo = threadClassInstance.GetType().GetMethod("Run");
        ThreadStart threadStart = (ThreadStart)Delegate.
          CreateDelegate(typeof(ThreadStart), threadClassInstance, methodInfo);
        thread = new Thread(threadStart);
      }
      catch(Exception) {
        throw;
      }

      return thread;
    }
  }
}

Hier das Interface


namespace Util.ThreadClass {
  /// <summary>
  /// Interface for using class threads
  /// </summary>
  public interface IThreadClass {
    /// <summary>
    /// The running thread method
    /// </summary>
    void Run();
  }
}

Verwendet kann das dann so werden:


MyThreadClass myThreadClass = new MyThreadClass(); // Must implement IThreadClass
Thread thread = ThreadClassUtil.GetThread(myThreadClass);
thread.Start(); // Run method in MyThreadClass will be invoked

Schlagwörter: Threadklasse, Thread

Viel Spass damit

Grüße

3.971 Beiträge seit 2006
vor 15 Jahren

Hallo mosspower,
schau dir lieber mal Entwurfsmuster für die asynchrone Programmierung an. Das ist besser als die schlechte Implementierung von Java.

Asynchronous Programming Overview
IAsyncResult Interface

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

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo mosspower,

mal von der Sinnhaftigkeit, die kleines_eichhoernchen angesprochen hat, abgesehen, frage ich mich, warum du das mit Reflection machst. Verwende als Parametertyp von GetThread einfach und besser IThreadClass und du kannst dir die Abfrage sparen, ob das Interface implementiert wird und außerdem die Run-Methode direkt und ohne Klimmzüge als ThreadStart-Methode verwenden.

herbivore

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

@myCSharp.de-Poweruser ,

danke, werde mich da mal einlesen (wenn ich dazukomme)

@herbivore,

stimmt, Du hast recht, vielen Dank. Interface übergeben und auf ThreadStart verzichten. Vielen Dank Leute, hier lernt man wirklich was 👍

5.299 Beiträge seit 2008
vor 15 Jahren

Hi!

Geht doch auch direkt:


   public class MyThreadClass  {

      public void Run() {
         throw new NotImplementedException();
      }

   }

      private void Test() {

         MyThreadClass myThreadClass = new MyThreadClass(); 
         Thread thread = new Thread(myThreadClass.Run);
         thread.Start(); // Run method in MyThreadClass will be invoked

      }

Der frühe Apfel fängt den Wurm.

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

@ErfinderDesRades ,

hast schon Recht, nur der Hintergrund ist, dass die Threads dynamisch mittels Reflection gestartet werden sollen 😉

Trotzdem Danke

5.742 Beiträge seit 2007
vor 15 Jahren

Hallo mosspower,

hast schon Recht, nur der Hintergrund ist, dass die Threads dynamisch mittels Reflection gestartet werden sollen

Warum muss es denn unbedingt Reflection sein?!?

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

Es MUSS natürlich nicht Reflection sein, jedoch hatte ich die Klasse entwickelt, um die Threads generisch erstellen zu können. ([EDIT]=herbivore] ... wofür, wie sich ja oben gezeigt hat, gar keine Reflection notwendig ist.[/EDIT]

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

OK,

das wieder mal ein Thread von mir, den man gleich wieder vergessen kann 😉 ... ohne eure Hilfe wäre das natürlich nicht möglich gewesen. Ich bedanke mich (ein bißchen beschämt 😉) bei euch für die Unterstützung.

Es geht mit folgendem Code ...


MyThreadClass myThreadClass = new MyThreadClass();
Thread thread = new Thread(((IThreadClass)myThreadClass).Run);
thread.Start();

Dafür braucht man natürlich keine Hilfsklasse

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo mosspower,

Es geht mit folgendem Code ...

es sollte sogar ohne den Cast auf IThreadClass gehen.

das wieder mal ein Thread von mir, den man gleich wieder vergessen kann

Vergessen würde ich nicht sagen. Er hat dir ja geholfen. Er passt nur nicht mehr in .NET-Komponenten und C#-Snippets. Ich habe ihn nach Basistechnologien und allgemeine .NET-Klassen verschoben.

herbivore

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

Hm,

jetzt wollte ich das bei mir einbauen.

Das Objekt serviceInstance ist generisch erstellt worden (Activator.CreateInstance), jetzt dachte ich, dass ich folgendermaßen vorgehen kann:


// Ich muss jetzt hier ja casten
Thread serviceThread = ((IThreadClass)serviceInstance).Run;

Das funzzt aber nicht, wegen dem Cast, würde ich MyThreadClass erstellen, dann würde es gehen. Wie mache ich denn das jetzt?

Die Fehlermeldung lautet:
Cannot convert method group Run to none delegate type Thread
Hm, genau wegen dieser Fehlermeldung hatte ich die "Hilfsklasse" gecoded.

5.299 Beiträge seit 2008
vor 15 Jahren

Thread serviceThread =new Thread( ((IThreadClass)serviceInstance).Run);

?

Der frühe Apfel fängt den Wurm.

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo mosspower,

wenn "Cannot convert method group ..." kommt, muss man normalweise einfach nur den konkreten Delegatentyp angeben:

Thread thread = new Thread(new ThreadStart(((IThreadClass)myThreadClass).Run));

herbivore

mosspower Themenstarter:in
456 Beiträge seit 2007
vor 15 Jahren
  
Thread serviceThread =new Thread( ((IThreadClass)serviceInstance).Run);  

?

ja, hatte ich doch tatsächlich vergessen. Lustig ist schon, dass ich es erst oben richtig geposted hatte. Ich hab keine Ahnung, was mit mir heute los ist.

Vielen Dank euch allen ... aus 25 Zeilen 1 gemacht 👍

3.971 Beiträge seit 2006
vor 15 Jahren

Nach dem asynchronen Entwurfsmuster von MS würde/könnte das Interface wiefolgt aussehen:


public interface IAsyncWorker {
  IAsyncResult BeginInvoke();
  void EndInvoke(IAsyncResult resultItem);
  void Invoke();
}

Was meiner Meinung nach besser zu bedienen ist als dein Interface. Außerdem ist diese Implementierung fasst jedem vertraut, beispielsweise enthält System.IO.Stream die gleichen Funktionen.

Zu verwenden wäre eine Instanz von diesem Interface wiefolgt:


(IThreadClass)serviceInstance).BeginInvoke();

Ein weitere Vorteil ist, dass diese Variante sich nicht dauerhaft an die Klasse System.Threading.Thread bindet sondern, je nach Implementierung auch mit dem ThreadPool oder BackgroundWorker oder beispielsweise einer Eigenimplementation wie Applikation mit Warteschlange funktioniert

Allerdings hat die Sache einen kleinen Haken. Laut MS (Jeffrey Richter), muss auch EndInvoke aufgerufen werden, wenn zuvor BeginInvoke aufgerufen wurde. Da je nach Implementierung bestimmte Ressourcen/Verweise erst nach EndInvoke aufgehoben werden. Wird EndInvoke somit nicht aufgerufen, ist es möglich, dass du dir deinen Speicher zumüllst.

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

5.299 Beiträge seit 2008
vor 15 Jahren

Hmm. Für meinen geringen Horizont ist das asynchronen Entwurfsmuster von MS für 90% der Fälle hoffnungslos überkandidelt. Da wird doch auch gefordert, daß ein asynchroner Aufruf cancelbar ist, nicht wahr? Und iwas von wegen eventbasiert war auch noch erwünscht, odr?
Ich muß gestehen, ich habe Threads noch nie für was anderes gebraucht, als mein Gui vorm Einfrieren zu bewahren, und da ham meine CrossThread-Calls bisher für hingelangt.
Also: nicht von Architektur-Astronauten einschüchtern lassen 😉

Der frühe Apfel fängt den Wurm.

3.971 Beiträge seit 2006
vor 15 Jahren

Also dass da was gefordert wird, mag ich bezweifeln. Wie bei allen Entwurfsmustern ist das ganze nur eine Richtlinie und muss nach Aufwand und Nutzen abgeschätzt werden - MS hat sich im .NET Framework auch nur teilweise an seine eigenen Richtlinien gehalten. Man kann natürlich das Interface auch zu


public interface IAsyncWorker {
  IAsyncResult BeginInvoke();
  void EndInvoke(IAsyncResult resultItem);
  void Invoke();
  void Cancel(IAsyncResult resultItem);

  event EventHandler<AsyncWorkerEventArgs> Completed;
}

public class AsyncWorkerEventArgs : EventArgs {
  public IAsyncResult Result { get; }
}

erweitern. Der Aufwand zur Implementierung wird aber größer (sowie die Fehleranfälligkeit). Man fängt sich hier allerdings auch ein neues Problem ein, der Thread der das Completed-Ereignis auslöst ist nicht der selbe der das Event abonniert hat bzw. der BeginInvoke aufgerufen hat, also muss auch hier entsprechend wieder synchronisiert werden, was die Performance wiederum nach unterdrückt.

Im Bereich Gui-Aktualisierung wäre das auch in der Tat mit Kanonen auf Spatzen geschossen, vor allem da das von MS mit dem Backgroundworker bereits in diese Richtung fertig implementiert wurde.

Das asynchrone Entwurfsmuster ist ein Ansatz, um mehreren Aufgaben zu parallelisieren und auf unterschiedliche Cores zu verteilen. Hat man nur einen CPU-Core ist diese Implementierung vllt. auch langsamer als eine gute prozedurale Implementierung. Bei Guis reicht es hingegen aus, Parallelität dem Anwender vorzugaukeln, ohne direkt die Kompletten Ressourcen ausschöpfen zu müssen

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

5.299 Beiträge seit 2008
vor 15 Jahren

Na, am Backgroundworker habich ja herumzumäkeln, dasses im Grunde noch Code von 2003 ist. Also die Chance, mit generischer Programmierung für Anwendungskomfort, Flexiblität und Typsicherheit zu sorgen, wurde sausen gelassen.
Statt dessen muß man üblicherweise Hilfsklassen für den Datentransport basteln, und dann in der Empfänger-Sub casten, und speziell strukturierten Code schreiben, damits mit dem BW zusammenpasst.

Aber das ist alles iwie noch so Gui-PillePalle - richtig lustig wirds wohl erst, wenn mehrere Threads sich gemeinsame Ressourcen teilen, und man da mit Apartments, VolatileWrite, Synclock, Thread.Join, WaitHandles und dergleichen zu Werke zu gehen hat.

( Äh, ich weiß, bin mal wieder ordentlich off topic. Aber wie beim letzten Mal: Die eigentliche Frage ist doch erledigt, dann kann man doch bischen fachsimpeln, fabulieren und schmutzige Witze reißen, odr? )

Der frühe Apfel fängt den Wurm.

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo ihr beiden,

interessantes Thema. An sich bin ich ja sehr für Abstraktion. Was die nebenläufige Programmierung angeht, habe ich z.B. schon verschiedentlich für meine synchronisierte Queue geworben (statt eine normale Queue zu nehmen und beim Zugriff darauf selbst zu synchronisieren). Gut keine gewaltige Abstraktion, aber immerhin wird die Synchronisation vollständig versteckt.

Insofern müsste ich eigentlich das asynchronen Entwurfsmuster von Microsoft total lieben. Tue ich aber nicht. Ich hatte es mir mal reingezogen, aber obwohl mich komplexes eigentlich nicht scheut, habe ich eine ganze Weile gebraucht, um das Muster zu verstehen. Soweit ich mich erinnere (ich habe das Muster nie benutzt und die Details deshalb auch schon wieder vergessen), wurden da sehr viele kleine, kaum voneinander zu unterscheiden Abstraktionsschritte verwendet, so dass es sehr schwer zu erkennen war, auf welcher Ebene man sich gerade befunden hat. Klar man könnte das Muster auch einfach nach Schema F implementieren, ohne sich darüber Gedanken zu machen, welche Zeile nun wofür gut ist. Aber auch das wollte mir nicht schmecken.

Nehmen wir mal das Entwurfsmuster von Microsoft für Events. Als ich mir das reingezogen habe, stellten sich mir auch verschiedene Fragen, z.B. warum die eventauslösende Methode On... und nicht Do... heißt. Aber bei dem Muster hat am Ende alles Hand und Fuß und jeder Bestandteil seinen Sinn. Und auch das mit dem On... hat letztlich einen nachvollziehbaren Grund. Zu diesem Schluss bin ich bei dem asynchronen Entwurfsmuster nicht gekommen.

Aber mal zurück zu der Threads-Klasse. Thread ist eigentlich eine sehr konkrete Klasse mit wenig Abstraktion. Und obwohl ich wie gesagt sehr für Abstraktion bin, benutze ich für nebenläufige Programmierung am liebsten Threads. Selbst da, wo das asynchronen Entwurfsmuster schon implementiert ist (z.B. in verschiedenen Framework-Klassen, die BeginXyz oder XyzAsync-Methoden anbieten) und ich es nur benutzen müsste, scheue ich mich das zu tun. Da erzeuge ich lieber einen eigenen Thread und rufe darin die synchronen Methode auf.

Dabei verwende ich auch bei nebenläuftiger Programmierung gerne abstrakte Konzepte, nicht nur die synchronisiere Queue. So verwende ich natürlich lock, um einen Bereich zu sperren und programmiere das nicht mit den konkreteren Semaphoren nach, obwohl das ginge und ich das könnte. Aber für Threads verwende ich immer direkt die Thread-Klasse. Ich schreibe noch nicht mal extra Klassen, die den Thread oder seinen Start kapseln. Ich verwende ganz konkreten Code, wie ihn auch mosspower am Ende verwendet hat:

MyThreadClass myThreadClass = new MyThreadClass();  
Thread thread = new Thread(((IThreadClass)myThreadClass).Run);  
thread.Start();  

Ich verwende nicht mal ThreadPool, sondern immer direkt die Thread-Klasse und bin auch sehr zufrieden damit. Vielleicht kann mir jemand, der bis hierher gelesen hat, sagen, warum ich überall gerne Abstraktionen verwende, nur bei Threads nicht. Mir selbst ist es leider unklar. 😃

herbivore

PS:
@ErfinderDesRades: Ganz passend finde ich den verlinkten Artikel über Architektur-Astronauten in Bezug auf das asynchronen Entwurfsmuster trotzdem nicht. Joel stört ja wohl vor allem Hype, der gerade vor dem Platzen der .COM-Blase gerne gemacht wurde und sagt nichts über Entwurfsmuster (im Sinne der GoF). Ich denke, den Artikel muss man als Auseinandersetzung mit den Auswüchsten der Internet-Euphorie Ende der 90er sehen.

Aber sehr abstrakt gesehen 😃 passt der Artikel dann natürlich doch in dem Sinne, dass man grundsätzlich aufpassen muss, dass Architektur nicht versucht, zu allgemein zu werden. Und beim asynchronen Entwurfsmuster könnte man schon den Eindruck haben, dass genau das passiert ist.

1.200 Beiträge seit 2007
vor 15 Jahren

Bei allem Respekt vor Joel Spolsky, aber ich glaube, sein Artikel ist eine Themaverfehlung. In der ersten Hälfte des Artikels, verwechselt er Features mit Architektur/Infrastruktur. Mal abgesehen davon, dass die Beispiele hinken. Peer to Peer Programme, die es möglich machten alles zu sharen, wurden wohl erfolgreicher als Napster. Gefunden haben die Leute weiterhin, was sie brauchten.

Dann kommen weitere Highlights wie:

Soap und WSDL mögen eine neue heiße Sache sein, aber sie ermöglichen mit Sicherheit nichts, was man nicht vorher mit dem Einsatz anderer Technologien bereits hätte tun können -- wenn man einen Grund gehabt hätte, es zu tun.

Ja, wieso auch einen Jumbo Jet entwickeln, wenn man einen Doppeldecker hat, der 2 Leute gleichzeitig befördern kann. Mehr braucht man ja nicht.

Es ist nett, dass wir XML nun als neues Austauschformat einsetzen können. Whoopee. Aber das ist ungefähr so interessant für mich, wie zu erfahren, dass mein Supermarkt Lastwagen verwendet, um Waren in das Lager zu transportieren. Gähn! Mangos, das ist es, was mich interessiert.

Das ist seine Conclusion. Er scheint gar nicht zu realisieren, dass er derjenige ist, der für den Transport verantwortlich ist. Ist ja schön und gut, dass ihn nur seine Mango interessiert. Aber aus der Endverbraucher Perspektive kann man doch nicht beurteilen, in wie fern die Organisation dieses Supermarktes von Lastwagen profitiert oder auch nicht.

Was Joel letztendlich sagt ist doch: "Ist doch egal, wie etwas gemacht ist, so lange es funktioniert. Blos nicht zu viel nachdenken, einfach tun."
Damit kann man Glück haben. Oder auch nicht. Dann tritt nämlich im schlimmsten Fall ein, dass Joel bald gar keine Mangos mehr kaufen kann, weil der Supermarkt pleite gemacht hat.

Wohingegen bei einer guten Organisation, der Supermarkt ihm wohl ziemlich bald 2 Mangos zum Preis von einer bieten könnte.

Alles in allem einer seiner schlechteren Artikel.

Shift to the left, shift to the right!
Pop up, push down, byte, byte, byte!

YARRRRRR!

5.742 Beiträge seit 2007
vor 15 Jahren

Alles in allem einer seiner schlechteren Artikel.

Das würde ich so nicht sagen.
Mit einigen Passagen kann ich zwar ebenfalls nicht übereinstimmen.

Allerdings verstehe ich den Artikel eher als stark übertriebenen Aufruf aus Usersicht, vor lauter Technologie und Konzepten das Ziel nicht zu verfehlen: Den Anwender.

Denn für diesen ist weniger die zugrundeliegende Technologie interessant, als die Vor- und Nachteile die ihm dadurch entstehen.

Und dies bringt der Artikel IMHO ganz gut rüber.

3.971 Beiträge seit 2006
vor 15 Jahren

Hallo Herbivore,
ich denke das größte Problem "deiner" Variante ist, das für jede asynchrone Aufgabe ein neuer Thread erstellt werden muss. Das Erstellen eines Threads selbst ist nicht grad sehr performant und es wird ohne das auch nur eine einzige Zeile Code ausgeführt wurde schonmal mind. 1MB an Hauptspeicher verwendet.

Bei der Verwendung des ThreadPools hat man den performanceverlust durch das Erstellen des Threads quasi nur einmal und der Thread kann wiederverwendet werden.

Die Nachteile vom ThreadPool sind allerdings, dass man die nützlichen Funktionen Abort und vorallem Join des Threads nicht nutzen kann. Diese lassen sich aber je nach Bedarf auch mittels den Funktionen Begin-/EndXYZ sowie CancelXYZ nachbilden - was quasi die Basis des asynchronen Entwurfsmusters ist (abgesehen von der Cancel-Variante)

Man hat anschließend (bei richtiger Implementierung) 4 Arten auf das Ende des asynchronen Vorgangs zu warten*EndInvoke *Polling von IAsyncResult.IsCompleted *CallBack-Delegaten bei BeginInvoke *Überladung von IAsyncResult.WaitHandle.WaitOne()

Das ganze ist meiner Meinung nach flexibler und besser skalierbarer als die Verwendung von Thread.Join(). Allerdings wäre die Implementierung Overkill bei "einfacher" Gui-Aktualisierung.

@ErfinderDesRades
ich hab mir das gestern bei MS nochmal angeschaut, MS unterscheidet ganz klar zwischen einer Eventbasierenden und einem BeginXYZ asynchronen Entwurfsmuster. Bei der Begin-Variante gibts die Möglichkeit ein Delegaten zu übergeben, der direkt aufgerufen wird, sobald die Operation abgeschlossen wurde, hat auch im Sinne von Event erstmal nix zu tun sondern nur als Callback-Funktion (die im übrigen null sein darf)

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

F
10.010 Beiträge seit 2004
vor 15 Jahren
49.485 Beiträge seit 2005
vor 15 Jahren

Hallo kleines_eichhoernchen,

ich denke das größte Problem "deiner" Variante ist, das für jede asynchrone Aufgabe ein neuer Thread erstellt werden muss.

nö 😃 Die synchronisierte Queue ist ja gerade dafür da, dass man einem Thread zu beliebigen Zeitpunkten neue Aufträge schicken kann. Dass ich am liebsten die Klasse Thread benutze, heißt ja nicht, dass ich für jede asynchrone Aufgabe einen neuen Thread erstelle.

Trotzdem hat mich dein Beitrag meiner Frage, warum ich die Thread-Klasse so liebe, näher gebracht. Denn damit hat man einfach die beste Kontrolle. Man kann eben entscheiden, wann man viele Threads startet und welchen man für was verwenden will.

herbivore

3.971 Beiträge seit 2006
vor 15 Jahren

nö 😃 Die synchronisierte Queue ist ja gerade dafür da, dass man einem Thread zu beliebigen Zeitpunkten neue Aufträge schicken kann. Dass ich am liebsten die Klasse Thread benutze, heißt ja nicht, dass ich für jede asynchrone Aufgabe einen neuen Thread erstelle.

Stimmt, hier würde ich auch keinen ThreadPool verwenden, da man von Ausgehen kann, das die Queue beim Programmstart erstellt wird und bis zum Programmende am Leben bleibt (somit auch der Thread selbst). Allerdings hat diese Queue einen Haken, man führt zwar eine Operation asynchron aus, parallelisiert diese aber nicht, da wenn du nur eine Queue verwendest die einzelnen Funktionen synchron hintereinander ausgeführt werden (Anwendungsfall, Gui-Aktualisierung). Die Queue lässt sich aber gut zur Thread-Sychronisation beispielsweise beim Zugriff auf STA-Com gebrauchen.

Anders sieht es aus, wenn du mehrere solche Queues in deiner Anwendung hast oder die Queue die einzelnen Funktionen an mehrere Threads verteilt - was an sich ja die ThreadPool-Klasse mehr oder weniger gut macht. Leider braucht man meist hier weitere Threadsynchronisationen wie lock, WaitHandle usw. um auf gemeinsame Ressourcen zuzugreifen.

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

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo kleines_eichhoernchen,

Allerdings hat diese Queue einen Haken, man führt zwar eine Operation asynchron aus, parallelisiert diese aber nicht,

Haken kann man das eigentlich nicht nennen. Erstmal ist das keine Eigenschaft der Queue selbst, sondern hängt - wie du quasi selbst scheibst - davon ab, ob man einen oder mehrere Worker-Threads aus der Queue lesen lässt. Aber selbst wenn man nur einen Thread verwendet, wie ich es geschrieben hatte, hängt die Frage, ob dieses Verhalten wirklich unerwünscht oder doch gerade erwünscht ist, davon ab, was man erreichen will. 😃

oder die Queue die einzelnen Funktionen an mehrere Threads verteilt

Meine Queue verteilt ja gar nichts, sondern ist rein passiv. Sie ist aber so implementiert, dass beliebig viele Threads aus ihr lesen können. Insofern ist mit ihr die Verteilung von Aufgaben an mehrere Threads möglich.

Leider braucht man meist hier weitere Threadsynchronisationen wie lock, WaitHandle usw. um auf gemeinsame Ressourcen zuzugreifen.

Genau, weshalb es eben durchaus erwünscht sein kann, nur einen Worker-Thread zu verwenden.

herbivore

3.971 Beiträge seit 2006
vor 15 Jahren

Meine Queue verteilt ja gar nichts, sondern ist rein passiv

Bei "passiven" Queues muss man wiederum aufpassen, dass sich nicht zu viele WorkerThreads um die Aufgaben schlagen müssen und einige am Ende leer ausgehen. Ein Beispiel wäre, es wird eine neue Aufgabe eingereiht, die Queue erweckt alle seine 10 WorkerThreads. Jeder dieser WorkerThreads schlägt sich nun um diese eine Aufgabe und zu 90% geht dieser auch noch leer aus. Der Synchronisationsaufwand ist dabei enorm hoch und jeder dieser Threads verbrät so etwas von der wertvollen Zeitscheibe - besonders kritisch,wenn die Anwendung doch nur auf einem PC mit einem CPU-Kern läuft.

Eine aktive Queue, die die einzelnen Aufgaben an Unter-Queues verteilt (passiv Queue, mit nur einem WorkerThread) kann die Aufgaben effektiver verteilen. Allerdings wird hier ein zusätzlicher Thread für die Verteilung gebraucht.

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

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo kleines_eichhoernchen,

Ein Beispiel wäre, es wird eine neue Aufgabe eingereiht, die Queue erweckt alle seine 10 WorkerThreads.

das Problem besteht bei meiner Queue nicht. 😃 Diese weckt bei einem Enqueue durch Monitor.Pulse nur genau einen Thread auf, egal wieviele warten. Eine aktive Queue wäre daher also nicht effektiver.

herbivore

3.971 Beiträge seit 2006
vor 15 Jahren

Ich Bezog mich ja auch auf mehrere WorkerThreads. Bei Einem spielt es schlicht weg keine Rolle 😉

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

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo kleines_eichhoernchen,

meine letzte Antwort bezog sich auch auf mehrere Worker-Threads. 😃

Diese weckt bei einem Enqueue durch Monitor.Pulse nur genau einen Thread auf, egal wieviele warten.

Meine Queue hat also das von dir beschriebene Problem, das bei schlecht implementierten passiven Queues bei Verwendung viele Worker-Threads in der Tat auftreten könnte, nicht. 😃

herbivore