Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ on loop conditions

Let's suppose I have the following piece of code:

IEnumerable<string> allKeys = _cache.Select(o => o.Key);
Parallel.ForEach(allKeys, key => _cache.Remove(key));

As you can see, I'm retrieving all the keys in _cache, storing them in my local variable allKeys, and then concurrently removing all the keys from _cache.

I would like however, to do it one single line. So what comes to mind is:

Parallel.ForEach(_cache.Select(o => o.Key), key => _cache.Remove(key));

But the statement _cache.Select(o => o.Key) would be called on each loop iteration, therefore retrieving different amount of elements each time (because I'm deleting them at the same time).

Is the latter line of code safe?

Does _cache.Select(o => o.Key) in loop statements, only get called once, and then each iteration uses the original result, or is it processed in every iteration step?

like image 296
Matias Cicero Avatar asked Jun 04 '15 13:06

Matias Cicero


3 Answers

As you can see, I'm retrieving all the keys in _cache, storing them in my local variable allKeys

No, you don't. Due to something called deferred execution, all you store is the command to get all your keys. You need to materialize this command to actually do what you think you do:

var allKeys = _cache.Select(o => o.Key).ToList();

That said: Is your cache thread safe? Why does it not feature a Clear method? Getting all keys and removing it by using multi threading seems like a not-so-great-idea.

If you insist to have it all in one line, you could use PLINQ:

_cache.Select(o => o.Key).AsParallel().ForAll(key => _cache.Remove(key));

But again: this seems to be a bad idea.

like image 166
nvoigt Avatar answered Nov 13 '22 10:11

nvoigt


First off, both codes are the same. There is no difference if you have a temporary variable or not.

Second: This code is flawed.

  1. LINQ uses deferred execution. In other words, while iterating allKeys, the underlying data - in your case _cache - is being iterated. In combination with the remove, this won't work.
  2. _cache most likely is a normal dictionary or something similar. In other words, it is not thread safe. UPDATE: According to a comment, it is of type ObjectCache and that type is indeed thread-safe. So this issue doesn't occur in your specific case.
like image 4
Daniel Hilgarth Avatar answered Nov 13 '22 09:11

Daniel Hilgarth


Would it not be more efficient to Dispose of your existing _cache object and just recreate it, rather than removing every item individually?

Saves querying and looping...

like image 1
Paddy Avatar answered Nov 13 '22 10:11

Paddy