Prüfungsantrag: Thread sichere Warteschlange (parallel Push pop)
-
30-09-2019 - |
Frage
Hier ist eine Implementierung einer Thread-sicher-Warteschlange. Die Push- und Pop nicht gegenseitig blockieren. Pop wird jedoch warten, bis ein Element gedrückt wird, wenn die Warteschlange leer ist. Mehrere Produzenten und Konsumenten können die Warteschlange verwenden. Bitte sagen Sie mir, wenn Sie irgendwelche Probleme zu sehen.
Update: Edited nach Antworten 1) gelöst, die Frage der „Warteschlange voll“ -Situation. 2) Es gibt BlockingCollection<T>
und ConcurrentQueue<T>
in .NET4. So gibt es keine Notwendigkeit, das Rad (für .NET4)
public class CustomQueue<T> where T: class
{
class Node
{
public Node()
{
Value = null;
NextNode = null;
}
public Node(T value)
{
Value = value;
NextNode = null;
}
public T Value;
public Node NextNode;
}
object PushLocker = new object();
object PopLocker = new object();
Semaphore QueueSemaphore;
volatile int PushIncrement;
volatile int PopIncrement;
int MaxItems;
Node FirstNode;
Node LastNode;
public CustomQueue(int maxItems)
{
QueueSemaphore = new Semaphore(0, maxItems);
MaxItems = maxItems;
FirstNode = LastNode = new Node();
PushIncrement = 0;
PopIncrement = 0;
}
public int Size()
{
return PushIncrement - PopIncrement;
}
public bool Push(T value)
{
lock(PushLocker)
{
if((Size()) >= MaxItems)
{
lock(PopLocker)
{
PushIncrement = PushIncrement - PopIncrement;
PopIncrement = 0;
return false;
}
}
Node newNode = new Node(value);
LastNode.NextNode = newNode;
LastNode = newNode;
PushIncrement++;
QueueSemaphore.Release();
return true;
}
}
public T Pop()
{
QueueSemaphore.WaitOne();
lock(PopLocker)
{
Node tempFirst = FirstNode;
Node tempNext = FirstNode.NextNode;
T value = tempNext.Value;
tempNext.Value = null;
FirstNode = tempNext;
PopIncrement++;
return value;
}
}
}
Lösung
Es sieht aus wie eine gute Umsetzung auf einem Blick. verschiedene Schlösser zu verwenden ist immer eine rote Fahne zu mir, damit ich auf einen guten harten Blick nahm einig der Rand Fälle, in denen gleichzeitig Anrufe an Pop
und Push
und es scheint sicher. Ich vermute, Sie erzogen sich wahrscheinlich auf der verknüpften Liste Implemenation einer Sperr Warteschlange nicht wahr? Der Grund, warum dies sicher ist, weil man immer nur Referenz LastNode
von Push
und FirstNode
von Pop
sonst der ganze Trick auseinander fallen würde.
Das einzige, was jetzt bei mir herausragt, ist, dass, wenn Sie versuchen, eine Zählung freizugeben von einem Semaphore
wird es eine Ausnahme auslösen, wenn es bereits voll ist, so dass Sie gegen das schützen möchten. 1 Andernfalls werden Sie mit zusätzlichen Knoten in der verknüpften Liste und die Warteschlange 1) haben am Ende mehr als die maximale Anzahl der Elemente und 2) es Live-gesperrt wird erhalten.
Update:
gab ich diesen etwas mehr Gedanken. Der Release
Aufruf in der Push
Methode sein wird, sehr problematisch. Ich sehe nur eine mögliche Lösung. Entfernen Sie den maxItems
Parameter aus dem Konstruktor und lassen Sie das Semaphore Int.MaxValue
zu zählen. Andernfalls Sie gehen radikal zu haben, um Ihren Ansatz in einer Implementierung resultierenden zu ändern, die keine ist, wo die Nähe zu dem, was Sie im Moment haben.
1 ich glaube, Sie werden feststellen, dass dies schwieriger sein wird als das, was Sie könnten sofort erkennen.
Andere Tipps
1.
Betrachten Sie einen zweiten Knoten Konstruktor hinzu:
public Node(T value)
{
Value = value;
}
dann Ihre Kundennummer ein:
Node newNode = new Node();
newNode.Value = value;
kann den Wert als unveränderlich behandeln:
Node newNode = new Node(value);
2.
dann Ihre öffentlichen Bereichen machen:
public T Value;
public Node NextNode;
in Auto Eigenschaften:
public T Value { get; private set; };
public Node NextNode { get; set; };
So kann man abstrakt die Nutzung von der Implementierung und fügt Validierung, eine andere Verarbeitung usw. nach der Tat mit minimaler Unterbrechung Client-Code.
Wenn Sie dies tun für Selbsterziehung, groß - sonst BlockingCollection<T>
oder ConcurrentQueue<T>
sind gute Alternativen.
Ein Problem, das ich hier sehe, ist, dass es keine Möglichkeit, Interrupt Pop
ist, sobald es beginnt - nimmt ein Objekt wartet, wenn er erwacht. Wie würden Sie diese nach unten auf Beendigung löschen? Ein TryPop
, dass die Renditen true
(mit einem Element) oder false
(wenn keine Daten) besser sein könnte, dann könnte man Fäden Signal warten sauber herunterzufahren, sobald die Warteschlange leer ist.