I am trying to convert an existing process in a way that it supports multi-threading and concurrency to make the solution more robust and reliable.
Take the example of an emergency alert system. When a worker clocks-in, a new Recipient object is created with their information and added to the Recipients collection. Conversely, when they clock-out, the object is removed. And in the background, when an alert occurs, the alert engine will iterate through the same list of Recipients (foreach), calling SendAlert(…) on each object.
Here are some of my requirements:
- Adding a recipient should not block if an alert is in progress.
- Removing a recipient should not block if an alert is in progress.
- Adding or removing a recipient should not affect the list of
recipients used by an in-progress alert.
I’ve been looking at the Task and Parallel classes as well as the BlockingCollection and ConcurrentQueue classes but am not clear what the best approach is.
Is it as simple as using a BlockingCollection? After reading a ton of documentation, I’m still not sure what happens if Add is called while I am enumerating the collection.
UPDATE
A collegue referred me to the following article which describes the ConcurrentBag class and how each operation behaves:
http://www.codethinked.com/net-40-and-system_collections_concurrent_concurrentbag
Based on the author’s explanation, it appears that this collection will (almost) serve my purposes. I can do the following:
-
Create a new collection
var recipients = new ConcurrentBag();
-
When a worker clocks-in, create a new Recipient and add it to the collection:
recipients.Add(new Recipient());
-
When an alert occurs, the alert engine can iterate through the collection at that time because GetEnumerator uses a snapshot of the collection items.
foreach (var recipient in recipients)
recipient.SendAlert(…); -
When a worker clocks-out, remove the recipient from the collection:
???
The ConcurrentBag does not provide a way to remove a specific item. None of the concurrent classes do as far as I can tell. Am I missing something? Aside from this, ConcurrentBag does everything I need.
ConcurrentBag<T>should definitely be the best performing class out of the bunch for you to use for such a case. Enumeration works exactly as your friend describes and so it should serve well for the scenario you have laid out. However, knowing you have to remove specific items from this set, the only type that’s going to work for you isConcurrentDictionary<K, V>. All the other types only offer aTryTakemethod which, in the case ofConcurrentBag<T>, is indeterminate or, in the case ofConcurrentQueue<T>orConcurrentStack<T>ordered only.For broadcasting you would just do:
The enumerator is once again a snapshot of the dictionary in that instant.