Thread-safe bags in .NET
July 21, 2015 1 Comment
Bags are very similar to Stacks and Queues. We saw that both stacks and queues order their elements in a well defined way: last-in-first-out and first-in-first-out respectively. Bags on the other hand are unordered collections. There’s no guarantee on how, i.e. in which order the elements will be retrieved.
Unlike stacks and queues, bags have no one-to-one single-threaded implementation in .NET. They are however implemented as thread-safe ConcurrentBag objects in the System.Collections.Concurrent namespace.
Here’s some terminology:
- You can insert new items to a concurrent bag with the Add method, like in the case of a List
- A single item is removed using the TryTake method. It returns false in case there was nothing to retrieve from the bag. If it returns true then the retrieved item is returned as an “out” parameter
- You can check the next item using the TryPeek method. It works the same way as TryTake but it doesn’t remove the element from the collection
The following example shows the ConcurrentBag in action. The code fills up a bag of integers with 1000 elements and starts 4 different threads that all read from the shared bag. There’s a very similar code example in the post on ConcurrentQueue and if you run both samples then you’ll see that ConcurrentBags and ConcurrentQueues behave very similarly in practice:
public class ConcurrentBagSampleService { private ConcurrentBag<int> _integerBag = new ConcurrentBag<int>(); public void RunConcurrentBagSample() { FillUpBag(1000); Task readerOne = Task.Run(() => GetFromBag()); Task readerTwo = Task.Run(() => GetFromBag()); Task readerThree = Task.Run(() => GetFromBag()); Task readerFour = Task.Run(() => GetFromBag()); Task.WaitAll(readerOne, readerTwo, readerThree, readerFour); } private void FillUpBag(int max) { for (int i = 0; i <= max; i++) { _integerBag.Add(i); } } private void GetFromBag() { int res; bool success = _integerBag.TryTake(out res); while (success) { Debug.WriteLine(res); success = _integerBag.TryTake(out res); } } }
Each thread will try to take items from the shared bag as long as TryTake returns false. In that case we know that there’s nothing more left in the collection.
View the list of posts on the Task Parallel Library here.
Pingback: Summary of thread-safe collections in .NET – .NET training with Jead