LINQ on a loop basis

Suppose I have the following code snippet:

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

As you can see, I am extracting all the keys in _cache , storing them in the local variable allKeys , and then simultaneously deleting all the keys from _cache .

I would like, however, to do this in one separate line. So what comes to mind:

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

But the statement _cache.Select(o => o.Key) will be called at each iteration of the loop, so it will extract a different number of elements each time (because I delete them at the same time).

Is the last line of code safe?

Is _cache.Select(o => o.Key) in loop statements, called only once, and then each iteration uses the original result or is processed at each stage of the iteration?

+6
source share
4 answers

Firstly, both codes are the same. It makes no difference if you have a temporary variable or not.

Second: this code has flaws.

  • LINQ uses deferred execution. In other words, when allKeys repeated allKeys underlying data - in your case _cache - is repeated. Combined with removal, this will not work.
  • _cache most likely a regular dictionary or something similar. In other words, it is not thread safe. UPDATE: According to the comment, it is of type ObjectCache , and this type is really thread safe. Therefore, this problem does not occur in your particular case.
+4
source

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

No no. Because of something called deferred execution, all you store is a command to get all your keys. You need to materialize this command in order to actually do what you think:

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

This says: is the cache thread safe? Why is there no Clear method in it? Getting all the keys and deleting them using multithreading does not seem like a very, very, ideal.

If you insist that it all be on the same line, you can use PLINQ:

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

But then again: this seems to be a bad idea.

+4
source

Wouldn't it be more efficient to Dispose existing _cache object and just recreate it rather than delete each element separately?

Saves query and loop ...

+1
source

Firstly, you cannot modify the collection, iterate over it, and this is what .Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector) does behind the scenes. So this line

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

just doesn't work.

You can try this

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

which will work with a copy of the keys. The ObjectCache type is thread safe, like MemoryCache , so you should be fine.

The only potential problem here is that this code is in a multi-threaded application (for example, in a web application). Having multiple threads that can read / write / delete to / from the cache opens a huge can of worms and makes it mandatory to use locks to control access to the cache.

0
source

Source: https://habr.com/ru/post/988514/


All Articles