2012-07-27 10 views
17

Ganz einfach: Anders als ConcurrentDictionary (die ich verwenden werde, wenn ich muss, aber es ist nicht wirklich das richtige Konzept), gibt es eine Concurrent-Sammlung (IProducerConsumer-Implementierung), die das Entfernen bestimmter Elemente basierend auf einfacher Gleichheit eines Elements oder unterstützt ein Prädikat, das eine Bedingung für das Entfernen definiert?Gleichzeitige Sammlung unterstützt das Entfernen eines bestimmten Artikels?

Erläuterung: Ich verfüge über einen mehrstufigen Arbeitsablaufalgorithmus mit mehreren Threads, der Objekte aus der Datenbank abruft und in eine "Start" -Warteschlange einfügt. Von dort werden sie von der nächsten Stufe gepackt, weiter bearbeitet und in andere Warteschlangen gestopft. Dieser Prozess wird in einigen weiteren Schritten fortgesetzt. In der Zwischenzeit wird die erste Stufe von ihrem Supervisor erneut aufgerufen und zieht Objekte aus der Datenbank heraus, und diese können Objekte enthalten, die noch in Bearbeitung sind (weil sie nicht fertig bearbeitet wurden und daher nicht mit der gesetzten Flagsgruppe erneut gesetzt wurden) sie sind fertig).

Die Lösung, die ich entwerfe, ist eine Master-Sammlung "in Arbeit"; Objekte gehen in diese Warteschlange, wenn sie zur Verarbeitung in der ersten Stufe abgerufen werden, und werden entfernt, nachdem sie in der Datenbank als "verarbeitet" gespeichert wurden, egal in welcher Phase des Arbeitsablaufs die erforderliche Verarbeitung abgeschlossen wurde. Solange sich das Objekt in dieser Liste befindet, wird es ignoriert, wenn es von der ersten Stufe erneut abgerufen wird.

Ich hatte geplant, eine ConcurrentBag zu verwenden, aber die einzige Entfernungsmethode (TryTake) entfernt ein beliebiges Element aus der Tasche, nicht eine bestimmte (und ConcurrentBag ist langsam in .NET 4). ConcurrentQueue und ConcurrentStack erlauben auch nicht das Entfernen eines anderen Elements als das nächste, das es Ihnen geben wird. ConcurrentDictionary bleibt bestehen, das funktioniert, ist aber mehr als ich brauche (alles was ich wirklich brauche, ist die ID der verarbeiteten Datensätze zu speichern; Sie ändern sich während des Workflows nicht).

+0

, wie fühlen Sie sich über ReaderWriterLockSlim verwenden und eine Liste? oder vielleicht rollen Sie Ihre eigene gleichzeitige Sammlung – Frobzig

+1

@Frobzig - Ambivalent zu mild interessiert. Ich mag die Concurrent-Sammlungen, weil sie einfach funktionieren. sehr wenig Code beteiligt. – KeithS

Antwort

14

Der Grund, warum es keine solche Datenstruktur gibt, ist, dass alle Sammlungen eine Nachschlageoperationszeit von O(n) haben. Dies sind IndexOf, Remove(element) usw. Sie alle zählen alle Elemente auf und prüfen sie auf Gleichheit.

Nur Hash-Tabellen haben eine Nachschlagezeit von O (1). In einem gleichzeitigen Szenario würde die Nachschlagezeit von O (n) zu einer sehr langen Sperre einer Sammlung führen. Andere Threads können während dieser Zeit keine Elemente hinzufügen.

Im Wörterbuch wird nur die von Hash betroffene Zelle gesperrt. Andere Threads können weiterhin hinzufügen, während einer durch Elemente in der Hash-Zelle auf Gleichheit prüft.

Mein Rat ist, gehen Sie weiter und verwenden Sie ConcurrentDictionary.


Übrigens, Sie haben Recht, dass ConcurrentDictionary ein bisschen überdimensioniert für Ihre Lösung ist. Was Sie wirklich brauchen, ist schnell zu überprüfen, ob ein Objekt in Arbeit ist oder nicht. A HashSet wäre dafür perfekt. Es tut im Grunde nichts weiter als Add(element), Contains(element), Remove(element).Es gibt eine ConcurrentHeshSet Implementierung in Java. Für C# habe ich folgendes gefunden: How to implement ConcurrentHashSet in .Net weiß nicht wie gut es ist.

Als ersten Schritt habe ich noch eine Hülle mit HashSet Schnittstelle um ConcurrentDictionary bringen es zum Laufen schreiben und dann verschiedene Implementierungen versuchen und Leistungsunterschiede sehen.

1

Es ist wirklich schwer, eine Sammlung Thread-Safe im generischen Sinne zu machen. Es gibt so viele Faktoren, die in die Thread-Sicherheit einfließen, die außerhalb der Verantwortung oder des Verantwortungsbereichs einer Bibliothek/Framework-Klasse liegen, die die Fähigkeit beeinträchtigen, wirklich threadsicher zu sein ... Einer der Nachteile, auf die Sie hingewiesen haben heraus ist die Leistung. Es ist unmöglich, eine performante Sammlung zu schreiben, die auch threadsicher ist, weil sie das Schlimmste annehmen muss ...

Die allgemein empfohlene Vorgehensweise ist, die gewünschte Sammlung zu verwenden und thread-sicher darauf zuzugreifen. Aus diesem Grund gibt es im Framework keine thread-sicheren Sammlungen mehr. Mehr dazu finden Sie unter http://blogs.msdn.com/b/bclteam/archive/2005/03/15/396399.aspx#9534371

+0

Der Unterschied zwischen einem Thread-sicheren Zugriff einer Sammlung und dem Zugriff auf eine Thread-sichere Sammlung kann ziemlich groß sein. Wenn die Anzahl der Threads groß ist und die Operationen, die sie ausführen, nicht trivial sind, kann es einen großen Engpass bei jedem Thread geben, der darauf wartet, dass seine eigene Sperre erfasst und gelöscht wird. Ich nehme an, Sie könnten eine beliebig große Sammlung von Sperrobjekten pflegen (entsprechend zB jedem Hash), aber das wäre ziemlich unordentlich ... –

+0

@PatrickM Die Menge von Operationen, um einen bestimmten Anwendungen Zugriff auf eine Sammlung thread-safe zu ermöglichen, ist viel niedriger als eine Sammlung Thread-sicher in jedem Szenario ist, kann verwendet werden. Sie sollten nur ein Sperrobjekt benötigen, um den Zugriff auf ein beliebiges Objekt (Sammlung oder nicht) Thread-sicher zu ermöglichen. Alle Concurrent * -Kollektionen verwenden nur ein Sperrobjekt. –

+0

In vielen Fällen ist es nicht schwierig, schnelle thread-sichere Sammlungen zu erstellen, die bestimmte Teilmengen der normalen Schnittstellen implementieren (z. B. eine 'IList ', die 'Add' und Schreiben-nach-Index als einzige Mutationsmethode unterstützt). Solche Dinge können effizienter sein als Sammlungen, die einen allgemeineren Zugang bieten, aber im Framework selten sind. Das einzige Beispiel, das Ihnen in den Sinn kommt, ist "ConditionalWeakTable", das wie ein Wörterbuch aussieht, aber nicht erlaubt, Elemente zu entfernen. – supercat

4

Wie bereits von anderen Posts erklärt ist es nicht möglich, Elemente aus einem Queue oder ConcurrentQueue standardmäßig zu entfernen, aber eigentlich ist der einfachste Weg, um zu umgehen, um das Element zu erweitern oder zu verpacken.

public class QueueItem 
{ 
    public Boolean IsRemoved { get; private set; } 
    public void Remove() { IsRemoved = true; } 
} 

Und wenn Ausreihen:

QueueItem item = _Queue.Dequeue(); // Or TryDequeue if you use a concurrent dictionary 
if (!item.IsRemoved) 
{ 
    // Do work here 
} 
+0

Diese Lösung verursacht Speicherverlust. –

+0

@KirillBestemyanov Nein, wie sollte es ein Speicherleck verursachen? –

+0

Die Aufgabe wird nicht aus der Sammlung gelöscht. Daher wird der von dieser Sammlung belegte Speicher nur erhöht, weil er ausgeführt wird. –

Verwandte Themen