过滤IEnumerable模式

时间:2010-02-02 22:43:16

标签: c# .net parallel-extensions

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

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表达式等的例子。)

3 个答案:

答案 0 :(得分:4)

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

但是,如果item.Foo恰好是昂贵的(我不会指望它,因为它是属性,但它始终是可能的),允许它并行化可能是有利的。

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

答案 1 :(得分:1)

如果我要实现这个,我只需要在调用foreach之前过滤列表。

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

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

注意:这可能是一种预先成熟的优化,并且不会对您的表现产生重大影响。

答案 2 :(得分:1)

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

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

但是阅读另一篇文章的评论我现在看到你已经在.Net 2.0了,所以有些事情可能有点棘手,不能超越编译器。

对于.Net 2.0,我认为你可以这样做(我有点不清楚,将方法名称作为代理传递仍然会起作用,但我认为会这样做):< / p>

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);