2010-06-12 7 views
74

Mit dem neuen ConcurrentBag<T> in .NET 4, wie entfernen Sie ein bestimmtes, spezifisches Objekt daraus, wenn nur TryTake() und TryPeek() verfügbar sind?So entfernen Sie ein einzelnes, spezifisches Objekt aus einem ConcurrentBag <>?

Ich denke an TryTake() und anschließend das Hinzufügen nur das resultierende Objekt zurück in die Liste, wenn ich nicht wollen, es zu entfernen, aber ich fühle mich wie ich etwas fehlen könnte. Ist das der richtige Weg?

Antwort

63

Die kurze Antwort: Sie können es nicht auf eine einfache Art und Weise tun.

Der ConcurrentBag behält eine Thread-lokale Warteschlange für jeden Thread und es sieht nur die Warteschlangen anderer Threads, sobald seine eigene Warteschlange leer ist. Wenn Sie einen Gegenstand entfernen und ihn zurückstellen, kann der nächste Gegenstand, den Sie entfernen, wieder der gleiche Gegenstand sein. Es gibt keine Garantie dafür, dass das wiederholte Entfernen und Zurücksetzen von Objekten es Ihnen ermöglicht, alle Elemente zu durchlaufen.

Zwei Alternativen für Sie:

  • alle Elemente entfernen und an sie erinnern, bis Sie das Sie entfernen möchten, finden, dann die anderen danach zurückstellen. Beachten Sie, dass wenn zwei Threads gleichzeitig versuchen, dies zu tun, Sie Probleme haben werden.
  • Verwenden Sie eine geeignetere Datenstruktur wie ConcurrentDictionary.
+2

SynchronizedCollection könnte auch ein geeigneter Ersatz sein entfernen. –

+0

@ILIABROUDNO - Sie sollten das als Antwort setzen! Das ist SO VIEL besser als ein kludsey ConcurrentDictionary, wenn Sie kein Dictionary benötigen – Denis

3

Wie Sie erwähnen, ist TryTake() die einzige Option. Dies ist auch das Beispiel auf MSDN. Reflektor zeigt auch keine anderen versteckten internen Methoden von Interesse.

13

Sie können nicht. Es ist eine Tasche, es ist nicht bestellt. Wenn du es zurücklegst, bleibst du einfach in einer Endlosschleife stecken.

Sie möchten ein Set. Sie können eins mit ConcurrentDictionary emulieren. Oder ein HashSet, dass Sie sich mit einem Schloss schützen.

+6

Bitte erweitern. Was würden Sie als Schlüssel im zugrunde liegenden ConcurrentDictionary verwenden? –

+2

Nun, ich nehme an, der Schlüssel wäre der Typ des Objekts, das Sie speichern möchten, und dann wäre der Wert eine Sammlung von einigen Arten. Das würde ein 'HashSet' emulieren, wie er beschreibt. –

-12

wie etwa:

bag.Where(x => x == item).Take(1); 

Es funktioniert, ich bin nicht sicher, wie effizient ...

+0

Dies entfernt nichts aus der Tasche. Der Artikel, den Sie abrufen, verbleibt in der Tasche. – Keith

+3

sollte sein "bag = new ConcurrentBag (bag.Where (x => x! = Artikel))" – atikot

+3

@atikot, diese Linie brachte mich zum Lachen – parek

-4
public static ConcurrentBag<String> RemoveItemFromConcurrentBag(ConcurrentBag<String> Array, String Item) 
{ 
    var Temp=new ConcurrentBag<String>(); 
    Parallel.ForEach(Array, Line => 
    { 
     if (Line != Item) Temp.Add(Line); 
    }); 
    return Temp; 
} 
1

Die ConcurrentBag groß ist, eine Liste zu behandeln, in denen Sie Elemente hinzufügen und Aufzählen von vielen Thread, dann werfen Sie es schließlich weg, wie der Name andeutet :)

As Mark Byers told, können Sie einen neuen ConcurrentBag, der nicht das Element enthält, das Sie entfernen möchten, bu t Sie müssen dies gegen mehrere Threads mit einer Sperre schützen. Dies ist ein Einzeiler:

myBag = new ConcurrentBag<Entry>(myBag.Except(new[] { removedEntry })); 

Dies funktioniert, und den Geist entsprechen, in dem die ConcurrentBag für konzipiert wurde.

+0

Ich denke, diese Antwort ist irreführend. Um dies klarzustellen, bietet dies KEINE Gewindesicherheit in dem gewünschten Entfernungsvorgang. Und wenn man ein Schloss umschließt, wird der Zweck, eine gleichzeitige Sammlung zu verwenden, nicht erreicht. –

+0

Ich stimme zu. Nun, um es ein wenig zu verdeutlichen, der ConcurrentBag soll gefüllt, nummeriert und mit seinem ganzen Inhalt weggeworfen werden, wenn er fertig ist. Jeder Versuch - auch meins - einen Gegenstand zu entfernen, führt zu einem schmutzigen Hack. Zumindest habe ich versucht, eine Antwort zu geben, obwohl das Beste darin besteht, eine bessere Concurrent Collection-Klasse zu verwenden, wie das ConcurrentDictionary. – Larry

2

Mark ist richtig, dass die ConcurrentDictionary wird so funktionieren, wie Sie wollen. Wenn Sie immer noch eine ConcurrentBag die folgenden verwenden möchten, nicht effizient Sie kümmern, werden Sie dort.

var stringToMatch = "test"; 
var temp = new List<string>(); 
var x = new ConcurrentBag<string>(); 
for (int i = 0; i < 10; i++) 
{ 
    x.Add(string.Format("adding{0}", i)); 
} 
string y; 
while (!x.IsEmpty) 
{ 
    x.TryTake(out y); 
    if(string.Equals(y, stringToMatch, StringComparison.CurrentCultureIgnoreCase)) 
    { 
     break; 
    } 
    temp.Add(y); 
} 
foreach (var item in temp) 
{ 
    x.Add(item); 
} 
1
public static void Remove<T>(this ConcurrentBag<T> bag, T item) 
{ 
    while (bag.Count > 0) 
    { 
     T result; 
     bag.TryTake(out result); 

     if (result.Equals(item)) 
     { 
      break; 
     } 

     bag.Add(result); 
    } 

} 
2

Dies ist meine Erweiterungsklasse, die ich in meinen Projekten verwende. Es kann ein einzelnes Element aus ConcurrentBag entfernen und kann auch Liste der Elemente aus dem Beutel

public static class ConcurrentBag 
{ 
    static Object locker = new object(); 

    public static void Clear<T>(this ConcurrentBag<T> bag) 
    { 
     bag = new ConcurrentBag<T>(); 
    } 


    public static void Remove<T>(this ConcurrentBag<T> bag, List<T> itemlist) 
    { 
     try 
     { 
      lock (locker) 
      { 
       List<T> removelist = bag.ToList(); 

       Parallel.ForEach(itemlist, currentitem => { 
        removelist.Remove(currentitem); 
       }); 

       bag = new ConcurrentBag<T>(); 


       Parallel.ForEach(removelist, currentitem => 
       { 
        bag.Add(currentitem); 
       }); 
      } 

     } 
     catch (Exception ex) 
     { 
      Debug.WriteLine(ex.Message); 
     } 
    } 

    public static void Remove<T>(this ConcurrentBag<T> bag, T removeitem) 
    { 
     try 
     { 
      lock (locker) 
      { 
       List<T> removelist = bag.ToList(); 
       removelist.Remove(removeitem);     

       bag = new ConcurrentBag<T>(); 

       Parallel.ForEach(removelist, currentitem => 
       { 
        bag.Add(currentitem); 
       }); 
      } 

     } 
     catch (Exception ex) 
     { 
      Debug.WriteLine(ex.Message); 
     } 
    } 
} 
Verwandte Themen