Ich verwende ConcurrentQueue
für eine gemeinsam genutzte Datenstruktur, deren Zweck darin besteht, die letzten N übergebenen Objekte zu speichern (Art des Verlaufs).
Angenommen, wir haben einen Browser und möchten die letzten 100 durchsuchten URLs haben. Ich möchte eine Warteschlange, die den ältesten (ersten) Eintrag beim Einfügen eines neuen Eintrags (Warteschlange) automatisch löscht (in die Warteschlange stellt), wenn die Kapazität voll ist (100 Adressen im Verlauf).
Wie kann ich das mit erreichen System.Collections
?
Antworten:
Ich würde eine Wrapper-Klasse schreiben, die bei Enqueue den Count überprüft und dann Dequeue, wenn der Count das Limit überschreitet.
quelle
q
ist für das Objekt privat, so dasslock
andere Threads nicht gleichzeitig darauf zugreifen können.Count
undTryDequeue
sind zwei unabhängige Operationen , die nicht von BCL Concurrent synched Pflege.ConcurrentQueue<T>
Objekt gegen einQueue<T>
Objekt mit geringerem Gewicht auszutauschen .Enqueue
rufen weiterhin die ursprüngliche Warteschlange auf. Mit anderen Worten, obwohl diese Antwort als akzeptiert markiert ist, ist sie vollständig und vollständig gebrochen.Ich würde mich für eine kleine Variante entscheiden ... ConcurrentQueue erweitern, um Linq-Erweiterungen für FixedSizeQueue verwenden zu können
quelle
Für alle, die es nützlich finden, gibt es hier einen Arbeitscode, der auf der obigen Antwort von Richard Schneider basiert:
quelle
Hier ist ein leichter kreisförmiger Puffer mit einigen Methoden, die für eine sichere und unsichere Verwendung gekennzeichnet sind.
Ich benutze gerne die
Foo()/SafeFoo()/UnsafeFoo()
Konvention:Foo
MethodenaufrufUnsafeFoo
als Standard.UnsafeFoo
Methoden ändern den Status frei ohne Sperre. Sie sollten nur andere unsichere Methoden aufrufen.SafeFoo
Methoden rufenUnsafeFoo
Methoden innerhalb einer Sperre auf.Es ist ein wenig ausführlich, aber es macht offensichtliche Fehler, wie das Aufrufen unsicherer Methoden außerhalb einer Sperre in einer Methode, die threadsicher sein soll, offensichtlicher.
quelle
Hier ist meine Einstellung zur Warteschlange mit fester Größe
Es wird eine reguläre Warteschlange verwendet, um den Synchronisierungsaufwand zu vermeiden, wenn die
Count
Eigenschaft für verwendet wirdConcurrentQueue
. Es wird auch implementiert,IReadOnlyCollection
damit LINQ-Methoden verwendet werden können. Der Rest ist den anderen Antworten hier sehr ähnlich.quelle
Nur zum Spaß, hier ist eine weitere Implementierung, von der ich glaube, dass sie die meisten Bedenken der Kommentatoren berücksichtigt. Insbesondere wird die Thread-Sicherheit ohne Sperren erreicht und die Implementierung wird von der Wrapping-Klasse ausgeblendet.
quelle
_queue.Enqueue(obj)
aber vorherInterlocked.Increment(ref _count)
, vorbelegt wird und der andere Thread aufruft.Count
? Es würde eine falsche Zählung bekommen. Ich habe nicht nach den anderen Problemen gesucht.Meine Version ist nur eine Unterklasse der normalen
Queue
. Nichts Besonderes, aber alle Teilnehmer zu sehen, und es passt immer noch zum Titel des Themas, den ich genauso gut hier einfügen könnte. Es gibt auch die in die Warteschlange gestellten für alle Fälle zurück.quelle
Fügen wir noch eine Antwort hinzu. Warum das über andere?
1) Einfachheit. Der Versuch, die Größe zu garantieren, ist gut und schön, führt jedoch zu unnötiger Komplexität, die ihre eigenen Probleme aufweisen kann.
2) Implementiert IReadOnlyCollection, dh Sie können Linq darauf verwenden und es an eine Vielzahl von Dingen übergeben, die IEnumerable erwarten.
3) Keine Verriegelung. Viele der oben genannten Lösungen verwenden Sperren, was bei einer Sammlung ohne Sperren falsch ist.
4) Implementiert die gleichen Methoden, Eigenschaften und Schnittstellen wie ConcurrentQueue, einschließlich IProducerConsumerCollection. Dies ist wichtig, wenn Sie die Auflistung mit BlockingCollection verwenden möchten.
Diese Implementierung könnte möglicherweise zu mehr Einträgen führen als erwartet, wenn TryDequeue fehlschlägt. Die Häufigkeit dieses Auftretens scheint jedoch keinen speziellen Code wert zu sein, der die Leistung zwangsläufig beeinträchtigt und eigene unerwartete Probleme verursacht.
Wenn Sie unbedingt eine Größe garantieren möchten, ist die Implementierung einer Prune () - oder ähnlichen Methode die beste Idee. Sie können eine ReaderWriterLockSlim-Lesesperre in den anderen Methoden (einschließlich TryDequeue) verwenden und nur beim Bereinigen eine Schreibsperre verwenden.
quelle
Für Ihr Codierungsvergnügen übermittle ich Ihnen das '
ConcurrentDeck
'Anwendungsbeispiel:
quelle
Nun, es hängt von der Verwendung ab, bei der ich festgestellt habe, dass einige der oben genannten Lösungen die Größe überschreiten können, wenn sie in einer Umgebung mit mehreren Threads verwendet werden. Mein Anwendungsfall war jedenfalls, die letzten 5 Ereignisse anzuzeigen, und es gibt mehrere Threads, die Ereignisse in die Warteschlange schreiben, und einen anderen Thread, der daraus liest und in einem Winform-Steuerelement anzeigt. Das war also meine Lösung.
BEARBEITEN: Da wir in unserer Implementierung bereits Sperren verwenden, benötigen wir ConcurrentQueue nicht wirklich. Dies kann die Leistung verbessern.
BEARBEITEN: Wir brauchen das
syncObject
obige Beispiel nicht wirklich und können lieber einqueue
Objekt verwenden, da wirqueue
in keiner Funktion neu initialisieren und esreadonly
sowieso markiert ist.quelle
Nur weil es noch niemand gesagt hat .. Sie können a verwenden
LinkedList<T>
und die Thread-Sicherheit hinzufügen:Zu beachten ist, dass die Standardreihenfolge für die Aufzählung in diesem Beispiel LIFO ist. Das kann aber bei Bedarf überschrieben werden.
quelle
Die akzeptierte Antwort wird vermeidbare Nebenwirkungen haben.
Die folgenden Links sind Referenzen, die ich verwendet habe, als ich mein Beispiel unten geschrieben habe.
Die Dokumentation von Microsoft ist zwar etwas irreführend, da sie eine Sperre verwenden, sie sperren jedoch die Segmentklassen. Die Segmentklassen selbst verwenden Interlocked.
quelle
Hier ist eine weitere Implementierung, die die zugrunde liegende ConcurrentQueue so oft wie möglich verwendet und gleichzeitig dieselben Schnittstellen bereitstellt, die über ConcurrentQueue verfügbar gemacht wurden.
quelle
Dies ist meine Version der Warteschlange:
Ich finde es nützlich, einen Konstruktor zu haben, der auf einem IEnumerable basiert, und ich finde es nützlich, einen GetSnapshot zu haben, um eine Multithread-Sicherheitsliste (in diesem Fall ein Array) der Elemente zum Zeitpunkt des Aufrufs zu haben, die nicht ansteigt Fehler, wenn sich die zugrunde liegende Sammlung ändert.
Die doppelte Zählprüfung soll unter bestimmten Umständen die Sperre verhindern.
quelle