PLINQ 真的不保持顺序吗?

Does PLINQ really not preserve order?

MSDN 据说 PLINQ 不保留顺序。我想在我的项目中演示它,但我有一个问题,那就是始终保持秩序。我有一个集合 List<Tuple<int, int>> table,这些是我的查询:

var linqQuery = table
            .Where(n => Enumerable.Range(2, (int) Math.Sqrt(n.Item1)).All(i => n.Item1 % i > 0))
            .Take(10)
            .ToList();

var plinqQuery = table
            .AsParallel()
            .Where(n => Enumerable.Range(2, (int) Math.Sqrt(n.Item1)).All(i => n.Item1 % i > 0))
            .Take(10)
            .ToList();

即使我不使用 .AsOrdered(),它们也会产生相同的有序结果。谁能告诉我为什么?

PLINQ 不保证项目将按顺序处理。它是否按顺序处理它们取决于几个因素。

首先,事先并不知道这些操作将在多少个线程中执行。在小例子中,很可能只使用一个或两个线程。

其次,请记住 Task 不等于线程。任务就是做什么。线程是 运行 的引擎。任务管理器选择将哪个任务放到哪个线程上。它不会 运行 一个新线程只是因为有任务在等待,除非有未使用的 CPU 资源真正 运行 该线程。

第三,即使有多个线程运行并行执行任务,任务完成的顺序也可能与它们开始时的顺序相同。

结论是 PLINQ 不会做任何事情来使任务 运行 按照它们初始化的相同顺序执行,真正的执行顺序是几个因素的结果。您的代码不应依赖于任何一个结果。

如果您只是想展示它,为什么不使用带有大型 Enumerable 集的简单 ForEach 与 ForAll?

例如:

using System;
using System.Linq;

namespace SO.MacakM.Answer
{
    class Program
    {
        static void Main(string[] args)
        {
            var range = Enumerable.Range(1, 1000);

            Console.WriteLine("Using LINQ...");
            range.ToList().ForEach(i => Console.WriteLine(i));

            Console.WriteLine("Using PLINQ...");
            range.AsParallel().ForAll(i => Console.WriteLine(i));

            Console.Read();
        }
    }
}

你可能会把顺序打乱在最后一个。 Test it in .NET Fiddle

考虑使用

   .AsOrdered()

示例:

   list.AsParallel().AsOrdered()

请注意,这会影响性能