考虑以下简单的代码模式:
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表达式等的例子。)
答案 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);