所以话题就是问题。

我得到的方法是AsParallel返回使用相同LINQ关键字的包装器ParallelQuery<TSource>,但是从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();
}

很明显,让我们转到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;
  }
}

问题是:它是如何工作的?我看不到由多个线程修改的变量的并发安全性,我们仅看到迭代器和求和。是魔术枚举器吗?或它是如何运作的? GetEnumerator()返回QueryOpeningEnumerator<TOutput>,但是它的代码太复杂了。

最佳答案

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

因此,TPL强制划分可枚举的源,然后对每个分区进行独立求和,然后对该求和进行总和,请参见IntSumAggregationOperatorEnumerator<TKey>。这里没有魔力,只是可以跳得更深。

10-04 22:24