假设我有以下代码:
IEnumerable<string> allKeys = _cache.Select(o => o.Key);
Parallel.ForEach(allKeys, key => _cache.Remove(key));
如您所见,我正在检索_cache
中的所有键,将它们存储在我的局部变量allKeys
中,然后同时从_cache
中删除所有键。
我想用一行来写。所以我想到的是:
Parallel.ForEach(_cache.Select(o => o.Key), key => _cache.Remove(key));
但是语句_cache.Select(o => o.Key)
将在每次循环迭代中调用,因此每次检索不同数量的元素(因为我同时删除了它们)。
后一行代码安全吗?
循环中_cache.Select(o => o.Key)
语句只被调用一次,然后每次迭代使用原始结果,还是在每个迭代步骤中处理?
可以看到,我正在检索_cache中的所有键,并将它们存储在本地变量allKeys
中
不,你不需要。由于所谓的延迟执行,您存储的只是获取所有键的命令。您需要实现这个命令,以便实际执行您认为应该执行的操作:
var allKeys = _cache。Select(o => o. key).ToList();
也就是说:你的缓存线程安全吗?为什么它没有一个Clear方法?通过使用多线程获取所有密钥并删除它似乎不是一个好主意。
如果你坚持要把它们都放在一行中,你可以使用PLINQ:
_cache.Select(o => o.Key).AsParallel().ForAll(key => _cache.Remove(key));
首先,两个代码是相同的。有没有临时变量是没有区别的。
第二:这个代码有缺陷。
- LINQ使用延迟执行。换句话说,当迭代
-
_cache
很可能是一个普通的字典或类似的东西。换句话说,它不是线程安全的。更新:根据注释,它的类型是ObjectCache
,并且该类型确实是线程安全的。所以这个问题不会发生在你的具体情况下。
allKeys
时,底层数据(在您的例子中是_cache
)正在被迭代。与remove结合使用,这将不起作用。它不是更有效的Dispose
你现有的_cache对象,只是重新创建它,而不是单独删除每个项目?
首先,您不能在遍历集合时修改它,这是.Select<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
在幕后所做的。所以这一行
Parallel.ForEach(_cache.Select(o => o.Key), key => _cache.Remove(key));
将无法工作。
你可以试试这个
Parallel.ForEach(_cache.Select(o => o.Key).ToList(), key => _cache.Remove(key));
,它将在密钥的副本上工作。ObjectCache
类型是线程安全的,MemoryCache
也是,所以你应该没问题。