AsParallel扩展如何实际工作

Ale*_*kiy 7 .net c# linq plinq task-parallel-library

所以主题是问题.

我得到那个方法AsParallel返回ParallelQuery<TSource>使用相同LINQ关键字的包装器,System.Linq.ParallelEnumerable而不是System.Linq.Enumerable

这很清楚,但是当我查看反编译源时,我不明白它是如何工作的.

让我们从最简单的扩展开始:Sum()方法.码:

[__DynamicallyInvokable]
public static int Sum(this ParallelQuery<int> source)
{
  if (source == null)
    throw new ArgumentNullException("source");
  else
    return new IntSumAggregationOperator((IEnumerable<int>) source).Aggregate();
}
Run Code Online (Sandbox Code Playgroud)

很明显,我们去找Aggregate()方法吧.它是InternalAggregate方法的一个包装器,可以捕获一些异常.现在让我们来看看它.

protected override int InternalAggregate(ref Exception singularExceptionToThrow)
{
  using (IEnumerator<int> enumerator = this.GetEnumerator(new ParallelMergeOptions?(ParallelMergeOptions.FullyBuffered), true))
  {
    int num = 0;
    while (enumerator.MoveNext())
      checked { num += enumerator.Current; }
    return num;
  }
}
Run Code Online (Sandbox Code Playgroud)

这是一个问题:它是如何工作的?我发现变量没有并发安全性,由许多线程修改,我们只看到迭代器和求和.这是魔术调查员吗?或者它是如何工作的?GetEnumerator()返回QueryOpeningEnumerator<TOutput>,但它的代码太复杂了.

Ale*_*kiy 3

最后,在我的第二次PLINQ 攻击中,我找到了答案。这很清楚。问题是枚举器并不简单。这是一个特别的multithreading。那么它是如何运作的呢?答案是enumerator不返回源的下一个值,它返回下一个分区的总和。因此,当实际求和工作在方法内部Environment.ProcessorCount执行时,此代码仅执行 2,4,6,8... 次(基于) 。enumerator.MoveNextenumerator.OpenQuery

因此,TPL 显然对源可枚举进行分区,然后对每个分区独立求和,然后执行此求和,请参阅IntSumAggregationOperatorEnumerator<TKey>。这里没有魔法,只是可以潜得更深。