【问题标题】:LINQ on loop conditionsLINQ 循环条件
【发布时间】:2015-08-19 03:23:27
【问题描述】:

假设我有以下代码:

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 =&gt; o.Key) 会在每次循环迭代时被调用,因此每次检索的元素数量都不同(因为我同时删除了它们)。

后一行代码安全吗?

_cache.Select(o =&gt; o.Key)在循环语句中,是否只被调用一次,然后每次迭代都使用原始结果,还是在每个迭代步骤中处理?

【问题讨论】:

  • _cache是​​什么类型的?
  • 在一行中这样做有什么好处?
  • “但是语句 _cache.Select(o => o.Key) 会在每次循环迭代时被调用” - 你确定吗?
  • @Paddy _cache 的类型为 ObjectCache Documentation
  • 不管并行度如何,您都不能在迭代枚举时对其进行变异。您需要在删除之前实现 allKeys

标签: c# .net linq loops iteration


【解决方案1】:

如您所见,我正在检索 _cache 中的所有键,并将它们存储在我的本地变量 allKeys 中

不,你没有。由于所谓的延迟执行,您存储的只是获取所有密钥的命令。你需要具体化这个命令才能真正做你认为你做的事情:

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

也就是说:你的缓存线程安全吗?为什么它没有 Clear 方法?使用多线程获取所有密钥并删除它似乎不是一个好主意。

如果您坚持将所有内容放在一行中,则可以使用 PLINQ:

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

但又一次:这似乎是个坏主意。

【讨论】:

    【解决方案2】:

    首先,两个代码是相同的。有没有临时变量没有区别。

    第二:这段代码有缺陷。

    1. LINQ 使用延迟执行。换句话说,在迭代时allKeys,底层数据——在你的例子中是_cache——正在被迭代。结合删除,这将不起作用。
    2. _cache 很可能是普通字典或类似的东西。换句话说,它不是线程安全的。更新:根据评论,它是ObjectCache 类型,并且该类型确实是线程安全的。因此,在您的具体情况下不会出现此问题。

    【讨论】:

    • 第一点将取决于数据结构。当然可能(但不太可能)有一个支持它的数据结构。后者是真正的交易破坏者。充其量,数据结构可以序列化删除,保证它的安全;它几乎没有办法真正并行工作。
    【解决方案3】:

    Dispose 现有的 _cache 对象并重新创建它而不是单独删除每个项目不是更有效吗?

    保存查询和循环...

    【讨论】:

    • 本来想处理掉的,但是我的缓存是MemoryCache类型的,所以根据这个帖子不推荐:How do I clear a System.Runtime.Caching.MemoryCache?
    • 好的,很有趣,但可能(也如那里的答案中提到的那样),只需将 _cache 设置为一个新实例就足够了吗?
    【解决方案4】:

    首先,您不能在迭代集合时对其进行修改,这是 .Select&lt;TSource, TResult&gt;(this IEnumerable&lt;TSource&gt; source, Func&lt;TSource, TResult&gt; 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 也是如此,所以你应该没问题。

    这里唯一的潜在问题是此代码是否位于多线程应用程序(如 Web 应用程序)中。拥有多个线程能够从缓存读取/写入/删除/从缓存中读取/写入/删除会引发大量蠕虫,并且强制使用锁来管理缓存访问。

    【讨论】:

      猜你喜欢
      • 2021-08-13
      • 2019-02-05
      • 2019-07-18
      • 2015-05-25
      • 1970-01-01
      • 2012-07-24
      • 2015-05-23
      • 2020-11-12
      • 2020-01-23
      相关资源
      最近更新 更多