Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct usage of ConcurrentBag?

I've already read previous questions here about ConcurrentBag but did not find an actual sample of implementation in multi-threading.

ConcurrentBag is a thread-safe bag implementation, optimized for scenarios where the same thread will be both producing and consuming data stored in the bag."

Currently this is the current usage in my code (this is simplified not actual codes):

private void MyMethod() {     List<Product> products = GetAllProducts(); // Get list of products     ConcurrentBag<Product> myBag = new ConcurrentBag<Product>();      //products were simply added here in the ConcurrentBag to simplify the code     //actual code process each product before adding in the bag     Parallel.ForEach(                 products,                 new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },                 product => myBag.Add(product));      ProcessBag(myBag); // method to process each items in the concurrentbag } 

My questions:
Is this the right usage of ConcurrentBag? Is it ok to use ConcurrentBag in this kind of scenario?

For me I think a simple List<Product> and a manual lock will do better. The reason for this is that the scenario above already breaks the "same thread will be both producing and consuming data stored in the bag" rule.
Also I also found out that the ThreadLocal storage created in each thread in the parallel will still exist after the operation (even if the thread is reused is this right?) which may cause an undesired memory leak.
Am I right in this one guys? Or a simple clear or empty method to remove the items in the ConcurrentBag is enough?

like image 984
hisoka21 Avatar asked Mar 20 '13 10:03

hisoka21


People also ask

What is the use of ConcurrentBag in 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.

How do I add a list in ConcurrentBag?

ConcurrentBag<int> ccBag = new ConcurrentBag<int>(); var listOfThings = new List<int>() { 1, 2, 4, 5, 6, 7, 8, 9 }; ccBag. AddRange(listOfThings);

What is a concurrent bag?

ConcurrentBag<T> is a thread-safe bag implementation, optimized for scenarios where the same thread will be both producing and consuming data stored in the bag.


2 Answers

This looks like an ok use of ConcurrentBag. The thread local variables are members of the bag, and will become eligible for garbage collection at the same time the bag is (clearing the contents won't release them). You are right that a simple List with a lock would suffice for your case. If the work you are doing in the loop is at all significant, the type of thread synchronization won't matter much to the overall performance. In that case, you might be more comfortable using what you are familiar with.

Another option would be to use ParallelEnumerable.Select, which matches what you are trying to do more closely. Again, any performance difference you are going to see is likely going to be negligible and there's nothing wrong with sticking with what you know.

As always, if the performance of this is critical there's no substitute for trying it and measuring.

like image 135
bmm6o Avatar answered Oct 14 '22 08:10

bmm6o


It seems to me that bmm6o's is not correct. The ConcurrentBag instance internally contains mini-bags for each thread that adds items to it, so item insertion does not involve any thread locks, and thus all Environment.ProcessorCount threads may get into full swing without being stuck waiting and without any thread context switches. A thread sinchronization may require when iterating over the collected items, but again in the original example the iteration is done by a single thread after all insertions are done. Moreover, if the ConcurrentBag uses Interlocked techniques as the first layer of the thread synchronization, then it is possible not to involve Monitor operations at all.

On the other hand, using a usual List<T> instance and wrapping each its Add() method call with a lock keyword will hurt the performance a lot. First, due to the constant Monitor.Enter() and Monitor.Exit() calls that each require to step deep into the kernel mode and to work with Windows synchronization primitives. Secondly, sometimes occasionally one thread may be blocked by the second thread because the second thread has not finished its addition yet.

As for me, the code above is a really good example of the right usage of ConcurrentBag class.

like image 27
Sergey Volodko Avatar answered Oct 14 '22 06:10

Sergey Volodko