3

In my code I got an ConcurrentDictionary now I want to iterate over each element in the Dictionary, but if a condition is true I want to remove an element from this Dictionary so I can't use a foreach loop. Also it might happen that the Dictionary will get a new element while in the loop or get one removed from a different thread. After some research I ended up using ElementAt.

Now my question is if the ConcurrentDictionary will release the indexes again like a List does. So that the first element will always have the index 0.

This is what my code looks like, CommandHandler.Timeouter is from ConcurrentDictionary:

int current = 0;

while (CommandHandler.Timeouter.Count() > current)
{
    var info = CommandHandler.Timeouter.ElementAt(current);
    var timeoutcooldown = info.Value.LastCommandTime.AddMinutes(1);

    if (timeoutcooldown < DateTime.UtcNow)
       {
           CommandHandler.Timeouter.TryRemove(info.Key, out _);
       }
    else current++;
}
13
  • 1
    Note that dictionaries do not guarantee a certain sequential order of elements (or key-value-pairs, for that matter). In other words, the order of elements in dictionaries is non-deterministic. If you ask whether an element at index 0 (as used by the Linq extension method ElementAt) will always stay at index 0, then no, this is not guaranteed... Commented Nov 11, 2018 at 10:05
  • Well this is bad.I guess I might end up using a List and using Linq to search for what I need. Commented Nov 11, 2018 at 10:07
  • 2
    You can iterate just fine through a ConcurrentDictionary (see here: stackoverflow.com/questions/17776422/…). If new elements are added to the ConcurrentDictionary by some other thread during an iteration then they might not be part of the iteration. But that isn't a problem as such new elements would then be subject of the next clean-up iteration a few minutes later. (1/2) Commented Nov 11, 2018 at 10:19
  • 1
    (2/2) ConcurrentDictionary also allow removal of elements within an iteration loop using ConcurrentDictionary.TryRemove (see here: stackoverflow.com/questions/2318005/…). See, StackOverflow has all the answers ;-) Commented Nov 11, 2018 at 10:19
  • 3
    Yes, but keep in mind to use ONLY methods provided by ConcurrentDictionary itself. No Linq or other extension methods! (because ConcurrentDictionary can ensure thread safety only with regard to its own methods) Commented Nov 11, 2018 at 10:21

1 Answer 1

4

ElementAt just treats the dictionary as an IEnumerable<KeyValuePair<TKey, TValue>>. Dictionaries are not ordered. The index is therefore meaningless. Think of the elements coming back in random order each time. Also, ElementAt has no way to make this thread safe.

It seems you want to implement cache expiration. Consider just using lock to access a normal dictionary. If there is not much contention this will be the simplest solution and very fast.

An alternative code pattern to this loop would be this:

var itemsToExpire = myDict.Where(/* compute expiration */).ToList();
foreach (var item in itemsToExpire)
   myDict.Remove(item);

No need for any complicated looping.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot for the answer, since elgonzo already guided me, I will not mark this as answer but an upvote for sure. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.