With reference from MSDN ConcurrentBag<T>::TryTake method.
Attempts to remove and return an object from the
ConcurrentBag<T>.
I am wondering about on which basis it removes object from the Bag, as per my understanding dictionary add and remove works on the basis of HashCode.
If concurrent bag has nothing to do with the hashcode, what will happen when the object values get change during the add and remove.
For example:
public static IDictionary<string, ConcurrentBag<Test>> SereverConnections
= new ConcurrentDictionary<string, ConcurrentBag<Test>>();
public class Student
{
public string FirstName { get; set; }
Student student = new Student();
SereverConnections.Add(student.FirstName{"bilal"});
}
// have change the name from student.FirstName="khan";
// property of the object has been changed
Now the object properties values has been changed.
What will be the behavior of when I remove ConcurrentBag<T>::TryTake method? how it will track the object is same when added?
Code:
public class Test
{
public HashSet<string> Data = new HashSet<string>();
public static IDictionary<string, ConcurrentBag<Test>> SereverConnections
= new ConcurrentDictionary<string, ConcurrentBag<Test>>();
public string SessionId { set; get; }
public Test()
{
SessionId = Guid.NewGuid().ToString();
}
public void Add(string Val)
{
lock (Data) Data.Add(Val);
}
public void Remove(string Val)
{
lock (Data) Data.Remove(Val);
}
public void AddDictonary(Test Val)
{
ConcurrentBag<Test> connections;
connections = SereverConnections["123"] = new ConcurrentBag<Test>();
connections.Add(this);
}
public void RemoveDictonary(Test Val)
{
SereverConnections["123"].TryTake(out Val);
}
public override int GetHashCode()
{
return SessionId.GetHashCode();
}
}
//calling
class Program
{
static void Main(string[] args)
{
Test test = new Test();
test.AddDictonary(test);
test.RemoveDictonary(test);//remove test.
test.RemoveDictonary(new Test());//new object
}
}
The exact behavior of the
ConcurrentBag<T>.TryTakemethod is not documented, so officially you are not allowed to make any assumption about which item will be removed. Any assumption that you make might be invalidated when the next version of the .NET runtime is released, since Microsoft reserves the right to change any undocumented behavior of any API without notice.The current implementation of the
ConcurrentBag<T>is based on something like aThreadLocal<Queue<T>>. When you add an item in the collection, it is enqueued in the local queue (the queue of the current thread). When you take an item from the collection, you are given an item dequeued from the local queue, or, if the local queue is empty, an item stolen from another thread's local queue. Here is an experimental demonstration of this behavior:There are 2 threads involved, the current thread and the thread
t. The current thread adds the items 1 - 5. The other thread adds the items 6 - 10. Then the current thread enumerates the collection, and finally it consumes it.Output:
Notice the difference between enumerating the collection and consuming it. The enumeration resembles a stack, and the consumption resembles a queue that midway becomes a stack. The first item that is taken (5) is the last item added by the current thread. The last item that is taken (10) is the last item added by the other thread. The consuming order would be different if the other thread had done it. In that case it would be:
Online demo.
At this point you might have realized that when the consumption order is important, the
ConcurrentBag<T>is not a suitable collection. The main application for this collection is for creating object-pools. If you want to reduce memory allocations by storing reusable objects in a pool, theConcurrentBag<T>might be the collection of choice. For practically any other use you are advised to stay away from this collection. Not only it's annoyingly unpredictable, but it is also equipped with a highly inefficient enumerator. Each time an enumeration starts, all the items in the collection are copied in a new array. So for example if the collection has 1,000,000 items and you callbag.First(), a newT[]array with 1,000,000 length will be created behind the scenes, just for returning a single item! A collection that is more suitable for use in general multithreading is theConcurrentQueue<T>. The methodConcurrentQueue<T>.Enqueueenqueues the item at the end of the queue, so it is much closer conceptually to the methodList<T>.Addthan the methodConcurrentBag<T>.Add.(I have posted more thoughts about the
ConcurrentBag<T>in this answer.)