2

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

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

4

3 回答 3

4

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

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

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

于 2010-02-02T23:06:28.443 回答
1

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

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

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

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

于 2010-02-02T22:50:30.903 回答
1

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

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);
于 2010-02-02T23:01:29.973 回答