Ich habe eine BlockingCollection . Producer-Aufgaben fügen Elemente hinzu, und Consumer-Tasks entfernen Elemente.
Jetzt möchte ich die Anzahl der Elemente in der Sammlung begrenzen, verwerfen alte Daten automatisch, wenn mehr Elemente hinzugefügt werden. Die Sammlung sollte nie mehr als die N zuletzt hinzugefügten Elemente gleichzeitig enthalten.
Wenn also die Produzenten neue Artikel schneller hinzufügen, als die Konsumenten sie entfernen, möchte ich, dass die Konsumenten nur die neuesten Artikel verarbeiten.
Ich kann die Größe einer BlockingCollection in ihrem Konstruktor begrenzen, aber das bedeutet natürlich nur, dass sie blockiert, wenn mehr Elemente hinzugefügt werden, und nicht, dass alte Elemente entfernt werden.
(Ich möchte nicht auf der Herstellerseite blockieren, nur die Verbraucherseite sollte blockieren, wenn sie Elemente aus einer leeren Sammlung abrufen)
Meine aktuelle Lösung ist ein Hack und funktioniert nur bei einer Größenbeschränkung von 1:
(Und ich bin nicht ganz sicher, dass es überhaupt zuverlässig funktioniert)
Gibt es eine sauberere Lösung?
Mach es so:
%Vor% Wenn die Warteschlange beim Hinzufügen des Elements voll ist, wird ein Element aus der Warteschlange entfernt. Es verwendet TryTake
, weil es möglich (unwahrscheinlich, aber möglich) ist, dass ein anderer Thread möglicherweise das letzte Element aus der Warteschlange entfernt hat, bevor Sie die Chance bekommen, eins zu nehmen.
Dies setzt natürlich voraus, dass Sie beim Erstellen von BlockingCollection
ein Limit für die Anzahl der Elemente festgelegt haben.
Eine weitere Möglichkeit, dies zu tun, ist, obwohl es mehr involviert ist, das Erstellen einer eigenen zirkulären Warteschlangenklasse, und Sie müssen die IProducerConsumerCollection Schnittstelle. Sie können dann eine Instanz dieser Klasse als Sicherungskopie für Ihre BlockingCollection
verwenden. Das Implementieren einer kreisförmigen Warteschlange ist nicht besonders schwierig, obwohl es Randfälle gibt, die schwierig zu korrigieren sind. Und Sie müssen es eine gleichzeitige Datenstruktur machen, obwohl das mit einer Sperre ziemlich einfach ist.
Wenn Sie nicht erwarten, dass die Warteschlange häufig überläuft, oder wenn die Warteschlange ziemlich wenig Verkehr hat (dh nicht tausende Male pro Sekunde getroffen wird), dann wird mein erster Vorschlag tun, was Sie wollen, und das wird nicht passieren ein Leistungsproblem. Wenn es ein Leistungsproblem gibt, ist die zirkuläre Warteschlange die Lösung.
Ich würde den Concurrent Stack verwenden:
Stellt eine threadsichere LIFO-Sammlung dar.
Und ich würde einen Gegenstand in den Stapel schicken, der Ihre Aufgabe umschließt und ihm einen Zeitstempel hinzufügt. Der Benutzer nimmt Aufgaben vom Stapel und verwerfen diejenigen mit einem Zeitstempel, der älter ist als ein von Ihnen definierter Schwellenwert.
Tags und Links c# multithreading thread-safety collections producer-consumer