【问题标题】:Filtering IEnumerable Pattern过滤 IEnumerable 模式
【发布时间】:2010-02-02 22:43:16
【问题描述】:

考虑以下简单的代码模式:

foreach(Item item in itemList)
{
   if(item.Foo)
   {
      DoStuff(item);
   }
}

如果我想使用 Parallel Extensions(PE) 并行化它,我可以简单地替换 for 循环结构,如下所示:

Parallel.ForEach(itemList, delegate(Item item)
{
   if(item.Foo)
   {
      DoStuff(item);
   }
});

但是,PE 将执行不必要的工作,将工作分配给 Foo 被证明为假的那些项目的线程。因此,我认为中间包装器/过滤 IEnumerable 在这里可能是一种合理的方法。你同意?如果是这样,实现这一目标的最简单方法是什么? (顺便说一句,我目前正在使用 C#2,所以我会感谢至少一个不使用 lambda 表达式等的示例。)

【问题讨论】:

    标签: c# .net parallel-extensions


    【解决方案1】:

    我不确定 .NET 2 的 PE 中的分区是如何工作的,所以很难说。如果每个元素都被推入一个单独的工作项(这将是一个相当糟糕的分区策略),那么提前过滤会很有意义。

    但是,如果 item.Foo 碰巧很昂贵(我不希望这样,因为它是一个属性,但它总是可能的),允许它被并行化可能是有利的。

    此外,在 .NET 4 中,TPL 使用的分区策略可以很好地处理这个问题。它专为处理不同工作水平的情况而设计。它在“块”中进行分区,因此一个项目被发送到一个线程,而是一个线程被分配一组项目,它批量处理。根据item.Foo 为假的频率,并行化(使用 TPL)很可能比预先过滤更快。

    【讨论】:

    • 我没有从这个角度考虑过。谢谢。很好的答案。
    • 如果您对 .NET 4 实现感兴趣,我实际上最近写了一篇关于 TPL 中工作分区策略的博客:reedcopsey.com/2010/01/26/…
    【解决方案2】:

    所有因素都归结为这一行:

    Parallel.ForEach(itemList.Where(i => i.Foo), DoStuff);
    

    但是阅读对另一篇文章的评论,我现在看到你还在 .Net 2.0 中,所以其中一些可能有点难以通过编译器。

    对于 .Net 2.0,我认为您可以这样做(我有点不清楚将方法名称作为委托传递仍然可以正常工作,但我认为可以):

    public IEnumerable<T> Where(IEnumerable<T> source, Predicate<T> predicate)
    {
       foreach(T item in source)
           if (predicate(item))
              yield return item;
    }
    
    public bool HasFoo(Item item) { return item.Foo; }
    
    Parallel.ForEach(Where(itemList, HasFoo), DoStuff);
    

    【讨论】:

    • 我会将i =&gt; DoStuff(i) 替换为DoStuff
    • 好点。我习惯于需要提供整个表达式语法,很容易忘记我可以缩短单个方法调用。完成。
    • 这让我觉得最好的计划是暂时保留代码,用 TODO 标记它,然后在我将项目升级到 C#3 时重新访问它。否则我会为了一点点收获而编写混乱的代码。
    • 这在带有 C# 2 的 .NET 2 中不起作用(如所写) - 您需要 C# 3 作为扩展方法(它可以在 .NET 2 上运行,但不能在 VS 2005 中编译)。 ..
    • 是的,忘记了。现已修复。
    【解决方案3】:

    如果我要实现这个,我会在调用 foreach 之前简单地过滤列表。

    var actionableResults = from x in ItemList WHERE x.Foo select x;
    

    这将过滤列表以获取可以操作的项目。

    注意:这可能是一个未成熟的优化,不会对您的性能产​​生重大影响。

    【讨论】:

    • 作为记录,PE 是单独发布到 .Net4 的,我目前在 .Net2 CLR 和 C#2 项目中使用它。
    • 如果您愿意在 C# 2 中使用 PE 的预发布端口,您应该不反对使用 LINQBridge 来添加 LINQ-to-Objects 支持...goo.gl/QuEC
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-20
    相关资源
    最近更新 更多