Laden...

Applikation mit Warteschlange [SyncQueue<T>-Klasse]

Erstellt von JuriG vor 15 Jahren Letzter Beitrag vor 14 Jahren 9.544 Views
J
JuriG Themenstarter:in
33 Beiträge seit 2008
vor 15 Jahren
Applikation mit Warteschlange [SyncQueue<T>-Klasse]

Hallo!

ich habe eine (vermutlich) sehr einfache Frage,

ich möchte in meiner c# Konsolenanwendung (die als Dienst läuft), eine Art Warteschlange integrieren,

d.h. Wenn ein Arbeitspaket reinkommt, wird er abgearbeitet, kommt noch ein Arbeitspaket währenddessen rein, wird er in die Warteschlange gepackt und erst später abgearbeitet.

Wie kann ich das am Besten erreichen?

Für die Hilfe wäre ich sehr dankbar!

630 Beiträge seit 2007
vor 15 Jahren

Hallo,

sieh dir mal die Klasse System.Collections.Generic.Queue an.

Gruss
tscherno

To understand recursion you must first understand recursion

http://www.ilja-neumann.com
C# Gruppe bei last.fm

49.485 Beiträge seit 2005
vor 15 Jahren

Hallo tscherno,

wobei die Queue<>-Klasse nicht synchronisiert ist. Und typischerweise werden de Aufträge von unterschiedlichen Threads eingestellt und ausgelesen. Ich habe neulich mal eine minimale, synchronisierte Queue (inkl. kleinem Testprogramm) geschrieben:


using System;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;

//*****************************************************************************
public class SyncQueue <T>
{
   //--------------------------------------------------------------------------
   private Queue <T> _q   = new Queue <T> ();

   //==========================================================================
   // <summary>Trägt einen Eintrag (ohne zu warten) ein</summary>
   public void Enqueue (T tItem)
   {
      lock (this) {
         _q.Enqueue (tItem);
         Monitor.Pulse (this);
         Debug.WriteLine (Thread.CurrentThread.Name + " Enqueue ==> " + _q.Count);
      }
   }

   //==========================================================================
   // <summary>
   //    Holt einen Eintrag aus der Queue heraus und wartet dabei nötigenfalls
   //    solange bis wieder ein Eintrag vorhanden ist.
   // </summary>
   public T Dequeue ()
   {
      lock (this) {
         while (_q.Count == 0) {
            Debug.WriteLine (Thread.CurrentThread.Name + " Wait");
            Monitor.Wait (this);
         }
         Debug.WriteLine (Thread.CurrentThread.Name + " Dequeue ==> " + (_q.Count - 1));
         return _q.Dequeue ();
      }
   }
}

//*****************************************************************************
static class App
{
   //-------------------------------------------------------------------------
   private static SyncQueue <int> _sq   = new SyncQueue <int> ();
   private static Random          _rand = new Random ();

   //==========================================================================
   public static void Main (string [] astrArg)
   {
      Debug.Listeners.Clear ();
      Debug.Listeners.Add (new ConsoleTraceListener ());

      for (int i = 0; i < 4; ++i) {
         Thread t = new Thread (RunDequeue);
         t.Name = "D" + i;
         t.Start ();
      }

      for (int i = 0; i < 4; ++i) {
         Thread t = new Thread (RunEnqueue);
         t.Name = "E" + i;
         t.Start ();
      }

   }

   //==========================================================================
   private static void RunEnqueue ()
   {
      for (int i = 1 ; i <= 25; ++i) {
         _sq.Enqueue (100);
         Thread.Sleep (_rand.Next (1000));
      }
   }

   //==========================================================================
   private static void RunDequeue ()
   {
      for (int i = 1 ; i <= 25; ++i) {
         _sq.Dequeue ();
         Thread.Sleep (_rand.Next (995));
      }
   }
}

herbivore

PS: Der Code ist jetzt auch unter SyncQueue <T> - Eine praktische Job-Queue verfügbar. Dort findet sich auch eine weitergehende Beschreibung der Klasse.

Suchhilfe: 1000 Worte

3.971 Beiträge seit 2006
vor 15 Jahren

Hallo Herbivore,
gibt es einen speziellen Grund, warum du einmal lock(this) verwendest und einmal Monitor.Enter(this).

Die Queue hat allerdings noch einen weiteren großen Nachteil, Sicherheitskontext (MSND: SecurityContext Class). Wenn beispielsweise ein Plugin einen Request hinzufügt, das Plugin aber selber nur einen geringen Sicherheitskontext hat, wird es mit dem der Warteschlange ausgeführt (bzw. mit dem Thread, der die Sachen herausholt und verarbeitet). Richtig wäre es den Sicherheitskontext beim hinzufügen festzuhalten und beim Ausführen des Requests dem Thread diesen Sicherheitskontext zu verpassen.

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,

gibt es einen speziellen Grund, warum du einmal lock(this) verwendest und einmal Monitor.Enter(this).

nein, deshalb verwende ich jetzt auch an beiden Stellen lock(this). Danke für den Hinweis.

Was den Sicherheitskontext angeht, kann das in dem von dir beschriebenen Fall natürlich eine Rolle spielen. Es gibt aber sicher mehr Fälle, in denen man das nicht berücksichtigen muss. Du kannst gerne eine Erweiterung schreiben, die den Sicherheitskontext berücksichtigt.

herbivore

C
252 Beiträge seit 2007
vor 14 Jahren

Aber erhält man nicht bereits mittels Queue.Synchronized() eine synchronisierte Queue. Wozu also eine selbst basteln?

49.485 Beiträge seit 2005
vor 14 Jahren

Hallo chavez,

grundsätzlich hast du recht. Man sollte das Rad nicht neu erfinden. Jedoch liefert Queue.Synchronized eine untypisierte Collection, wogegen meine Queue generisch und damit typsicher ist. Der entscheidende Unterschied ist aber, dass Queue.Dequeue eine Exception wirft, wenn die Queue leer ist, statt wie meine Queue zu warten, bis ein Element mit Enqueue hinzugefügt wurde. Also genau das, was man für eine Job-Queue braucht.

herbivore