假设我有以下代码:
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)
,只调用一次,然后每次迭代使用原始结果,还是在每个迭代步骤中处理?
答案 0 :(得分:4)
首先,两个代码都是相同的。如果你有一个临时变量没有区别。
第二:这段代码存在缺陷。
allKeys
时,底层数据(在您的情况下为_cache
)正在被迭代。与删除相结合,这不会起作用。_cache
最有可能是普通词典或类似的词典。换句话说,它不是线程安全的。更新:根据评论,它是ObjectCache
类型,并且该类型确实是线程安全的。因此,在您的具体情况下,此问题不会发生。答案 1 :(得分:4)
正如您所看到的,我正在检索_cache中的所有键,将它们存储在我的本地变量allKeys
中
不,你没有。由于称为延迟执行的内容,您存储的所有内容都是获取所有密钥的命令。您需要具体化此命令才能实际执行您认为的操作:
var allKeys = _cache.Select(o =&gt; o.Key) .ToList();
那说:你的缓存线程安全吗?为什么它没有Clear方法?获取所有密钥并使用多线程删除它似乎是一个不那么好主意。
如果你坚持让所有这一切都在一行,你可以使用PLINQ:
_cache.Select(o => o.Key).AsParallel().ForAll(key => _cache.Remove(key));
但是又一次:这似乎是一个坏主意。
答案 2 :(得分:1)
现有的_cache对象的Dispose
不是更有效率而只是重新创建它,而不是单独删除每个项目吗?
保存查询和循环...
答案 3 :(得分:0)
首先,你不能在迭代时修改一个集合,这就是.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));
将在密钥的副本上工作。 <{1}}类型是线程安全的,ObjectCache
也是如此,所以你应该没问题。
此处唯一的潜在问题是此代码是否位于多线程应用程序(如Web应用程序)中。让多个线程能够读取/写入/从缓存中删除会打开大量蠕虫,并使用锁来强制管理缓存访问。