Wie entferne ich alle Artikel aus ConcurrentBag?

Antworten:

37

Obwohl dies aufgrund einer möglichen Rennbedingung möglicherweise nicht vollständig klar ist, ist dies ausreichend:

while (!myBag.IsEmpty) 
{
   myBag.TryTake(out T _);
}
Daniel A. White
quelle
23
Das ist definitiv nicht atomar. Sollte also nicht als Erweiterungsmethode auf ConcurrentBag hinzugefügt werden, da alle anderen Methoden unter der Annahme eines atomaren Zugriffs verwendet werden.
Anupam
Kann TryTake nicht auch aus anderen Gründen fehlschlagen, als wenn es leer ist?
BrainSlugs83
21
Dies ist sehr gefährlich. Wenn ein anderer Prozess kontinuierlich Elemente hinzufügt, kann dies beschäftigt bleiben.
IvoTops
4
Die Antwort von @Adam Houldsworth + mein Kommentar ist besser.
Chris Marisic
1
Wenn dies eine gefährliche Lösung ist, warum wird sie dann immer noch akzeptiert?
Barış Akkurt
65

Update 10/03/2017: Wie @Lou richtig hervorhebt, ist die Zuweisung atomar. In diesem Fall Schaffung der ConcurrentBagnicht atomar sein, aber diese Bezugnahme in die Variable setzen wird atomar sein - so Sperren oder Interlocked.Exchangeum ist es nicht unbedingt erforderlich ist .

Einige weiterführende Literatur:

Die Referenzzuweisung ist atomar. Warum wird Interlocked.Exchange (ref Object, Object) benötigt?

Ist eine Referenzzuweisung threadsicher?


Sie können jederzeit den Zugriff auf die Tasche selbst sperren und eine neue Instanz davon erstellen. Gegenstände in der Tasche sind dann für GC berechtigt, wenn nichts anderes daran festhält:

lock (something)
{
    bag = new ConcurrentBag();
}

Oder wie Lukazoid betont:

var newBag = new ConcurrentBag();
Interlocked.Exchange<ConcurrentBag>(ref bag, newBag);

Eine einfache Möglichkeit, den Inhalt abzulegen, setzt jedoch voraus, dass ein Objekt, wenn es Zugriff haben möchte, auch die Sperre erhält - dies kann teuer sein und die in sich ConcurrentBagselbst vorgenommene Leistungsoptimierung negieren .

Wenn Sie wissen, dass zu diesem Zeitpunkt nichts anderes auf die Tasche zugreifen wird, beten Sie sie an und schließen Sie sie nicht ab :-)

Adam Houldsworth
quelle
Wäre es nicht eine bessere Lösung, einfach eine Kopie der Referenz der Tasche an einem Ort zu nehmen, an dem Sie die Tasche konsumieren, dann können Sie dies nur bag = newungestraft tun ?
Chris Marisic
1
@ChrisMarisic Ja, wenn Sie die gemeinsame Nutzung von Daten vermeiden können, sind Sie frei von diesen Problemen. Die Frage hatte jedoch nicht viel Kontext.
Adam Houldsworth
10
Interlocked.Exchangekann besser sein als ein Schloss
Lukazoid
5
Wie funktioniert es Interlocked.Exchange, wenn zum Zeitpunkt des Austauschs ein anderer Thread zum Beutel hinzugefügt wird? Wird das Addwährend der gesperrt Exchange?
Brandon
2
Zuweisungen sind in .NET atomar, sowohl die Sperre als Interlocked.Exchangeauch redundant (und bieten keine Thread-Sicherheit).
Lou
24

Die ausgewählte Antwort ist eine Art Problemumgehung, daher füge ich meine eigene Problemumgehung hinzu.

Meine Lösung bestand darin, alle verfügbaren Sammlungen im System.Collections.Concurrent- Namespace zu durchsuchen , um einen zu finden, in dem es trivial war, alle Elemente aus der Sammlung zu löschen.

Die ConcurrentStack- Klasse verfügt über eine Clear () -Methode, mit der alle Elemente aus der Auflistung entfernt werden. Tatsächlich ist dies (derzeit) die einzige Sammlung im Namespace, die dies tut. Ja, Sie müssen Push(T element)statt Add(T element), aber ehrlich gesagt ist das die Zeitersparnis wert.


quelle
1
Es gibt jedoch noch viele andere wichtige Unterschiede zwischen den Sammlungen. Wenn Sie beispielsweise feststellen müssen, ob sich ein bestimmtes Objekt in der Sammlung befindet, ist es mit einer Tasche sowohl trivial als auch effizient, jedoch nicht mit einem Stapel.
Servy
@Servy: Ja aber trotzdem. Nein, eigentlich habe ich angefangen, eine Pro / Contra-Liste zu schreiben, aber es hängt wirklich davon ab, was Ihre Anforderungen sind. Gleichzeitige Sammlungen eignen sich beispielsweise für den Multithread-Zugriff, aber keine von ihnen ermöglicht Ihnen die Indizierung in die Sammlung, was Sie möglicherweise daran hindert, sie zu verwenden. Die Frage betraf speziell das Löschen eines gleichzeitigen Beutels (weshalb ich darauf gestoßen bin), und das triviale Löschen der Sammlung mit Fadensicherheit war meine Anforderung. Meine Antwort war, Sammlungen zu wechseln.
2
Ich benutze auch gleichzeitigen Stapel anstelle von Tasche. Es ist seltsam, dass der Stapel Clear und Bag nicht hat. Hauptzweck der Tasche ist es, Werte zu speichern, die Existenz zu überprüfen und alle oder einzelne zu entfernen. Der gleichzeitige Stapel wird so etwas wie "ein bisschen begrenzter realer gleichzeitiger Beutel".
Maxim
@Servy Wie können Sie effizient feststellen, ob ein bestimmtes Element in einem enthalten ist ConcurrentBag? Ich kann keine native Eigenschaft oder Methode dafür sehen. Das Containszählt nicht. Es ist eine Erweiterungsmethode für generische IEnumerables und alles andere als effizient.
Theodor Zoulias
9

Im Sinne von Problemumgehungen .. ConcurrentDictionary<T, bool>hat ein atomares Clear, ermöglicht es Ihnen aber auch schnell zu überprüfen, ob ein Schlüssel vorhanden ist. "Schnell" ist natürlich ein relativer Begriff, aber abhängig von Ihrer Verwendung ist er möglicherweise schneller als das Aufzählen eines großen Stapels.

OlduwanSteve
quelle
Schön! Dies sollte als der für solche Fälle ausgewählte Containertyp betrachtet werden. ConcurrentStack ähnlich.
Latenz
3

Ab .NET Core 2.0 / .NET Standard 2.1 / .NET Framework 5.0 gibt es eine Clear()Methode für ConcurrentBag<T>. Siehe: ConcurrentBag.Clear .

Olabacker
quelle
-2
int cnt = _queue.Count;
for (; cnt > 0; cnt--)
{
     _queue.TryDequeue(out img);
}

Es fällt nicht in eine Endlosschleife und löscht den Inhalt der Gegenwart.

a_pcnic
quelle