首页 > 解决方案 > ParallelEnumerable.Zip() 是如何工作的,为什么调用 AsEnumerable() 时行为会发生变化?

问题描述

我试图了解与andParallelEnumerable.Zip()结合使用的行为。IEnumerable<T>AsEnumerable()

给定下面的代码parallelResultandresult将不一样。假设ParallelEnumerable.Zip()意味着我只想随机配对项目,这是有道理的。但是,如果我取消注释AsEnumerable(),则parallelResult.ShouldBe(result)不再抛出,即使是非常大n且重复的运行。如果两个输入都是Lists,则断言也通过。

预期的行为是什么?我们在调用时是否依赖于实现细节AsEnumerable()

int n = 200;
IReadOnlyList<double> list = Enumerable.Range(0, n).Select(x=>(double)x).ToList();
IEnumerable<double> enumerable = Enumerable.Range(10, n).Select(x => (double)x);

var result = list
    .Zip(enumerable, Tuple.Create)
    .OrderBy(x=>x.Item1)
    .ThenBy(x=>x.Item2)
    .ToList();

var parallelResult = list
    .AsParallel()
    .Zip(enumerable.AsParallel(), Tuple.Create)
    // .AsEnumerable()
    .OrderBy(x => x.Item1)
    .ThenBy(x => x.Item2)
    .ToList();

parallelResult.ShouldBe(result);

我试过阅读文档,但和来源,但这并没有让我更聪明。尤其是源代码很难理解(这是高度优化的并行代码所期望的)。

标签: c#.netlinqparallel-processing

解决方案


正如 NetMage 指出的PLINQ 文档中关于排序状态的文档,该文档Zip()属于组中的运算符“以下 PLINQ 查询运算符在某些情况下可能需要有序的源序列才能产生正确的结果: ”。

因此AsOrdered(),如果代码多次运行,则必须使用它来保证每对中都有相同的项目。要获得有序结果,上面的示例必须如下所示

int n = 200;
IReadOnlyList<double> list = Enumerable.Range(0, n).Select(x=>(double)x).ToList();
IEnumerable<double> enumerable = Enumerable.Range(10, n).Select(x => (double)x);

var result = list
    .Zip(enumerable, Tuple.Create)
    .OrderBy(x=>x.Item1)
    .ThenBy(x=>x.Item2)
    .ToList();

var parallelResult = list
    .AsParallel()
    .AsOrdered()
    .Zip(enumerable.AsParallel().AsOrdered(), Tuple.Create)
    .OrderBy(x => x.Item1)
    .ThenBy(x => x.Item2)
    .ToList();

parallelResult.ShouldBe(result);

所有其他看似可重复结果的情况只是巧合,或者是由于实施细节可能发生变化。


推荐阅读