Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to remove a single, specific object from a ConcurrentBag<>?

Tags:

c#

c#-4.0

People also ask

How do you empty ConcurrentBag?

The ConcurrentStack class has a Clear() method which removes all elements from the collection. In fact, it's the only collection in the namespace (currently) that does. Yes, you have to Push(T element) instead of Add(T element) , but frankly that's worth the time saved.

What is ConcurrentBag C#?

The ConcurrentBag is one of the thread safe collections that was introduced in . NET 4.0. This collection allows us to store objects in an unordered manner and allows for duplicates. It is useful in a scenario where we do not need to worry about the order in which we would retrieve the objects from the collection.


The short answer: you can't do it in an easy way.

The ConcurrentBag keeps a thread local queue for each thread and it only looks at other threads' queues once its own queue becomes empty. If you remove an item and put it back then the next item you remove may be the same item again. There is no guarantee that repeatedly removing items and putting them back will allow you to iterate over the all the items.

Two alternatives for you:

  • Remove all items and remember them, until you find the one you want to remove, then put the others back afterwards. Note that if two threads try to do this simultaneously you will have problems.
  • Use a more suitable data structure such as ConcurrentDictionary.

You can't. Its a bag, it isn't ordered. When you put it back, you'll just get stuck in an endless loop.

You want a Set. You can emulate one with ConcurrentDictionary. Or a HashSet that you protect yourself with a lock.


The ConcurrentBag is great to handle a List where you can add items and enumerate from many thread, then eventually throw it away as its name is suggesting :)

As Mark Byers told, you can re-build a new ConcurrentBag that does not contains the item you wish to remove, but you have to protect this against multiple threads hits using a lock. This is a one-liner:

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

This works, and match the spirit in which the ConcurrentBag has been designed for.


Mark is correct in that the ConcurrentDictionary is will work in the way you are wanting. If you wish to still use a ConcurrentBag the following, not efficient mind you, will get you there.

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);
}

As you mention, TryTake() is the only option. This is also the example on MSDN. Reflector shows no other hidden internal methods of interest either.