PLinq
并行Linq(PLinq)是Linq模式的并行实现。PLinq查询类似普通的Linq查询,但它会将数据源分成片段,然后在多个处理器上对单独工作线程上的每个片段并行执行查询。
System.Linq.ParallelEnumerable 类公开了几乎所有的 PLINQ 功能。 它和 System.Linq 命名空间类型的其余部分一起被编译到 System.Core.dll 程序集中。 Visual Studio 中默认的 C# 和 Visual Basic 项目均会引用该程序集并导入该命名空间。
使用PLinq
针对任何一个Enumerable
对象使用AsParallel()
方法即可得到并行的查询。
Array arr1 = Array.CreateInstance(typeof(int), 100);
for (int i = 0; i < arr1.Length; i++)
arr1.SetValue(i, i);
var res1 = from int x in arr1.AsParallel() where x % 2 == 0 select x;
注意:AsParallel()
是PLinq的入口点。
执行模式
PLinq在分析查询的总体结构之后会选择并行化查询或者顺序化查询。使用WithExecutionMode
方法和System.Linq.ParallelExecutionMode
枚举中的ForceParallelism
字段强制并行化整个查询。
并行度
使用WithDegreeOfParallelism
方法指示PLnq使用不超过指定数量的处理器。
var res2 = from x in arr1.AsParallel().WithDegreeOfParallelism(2)
where x > 42
select x;
保留源序列排序结果
AsOrdered
方法指定PLinq应为查询的其余部分保留源序列的排序,或直到通过使用orderby
子句更改排序为止。AsUnordered
指定保留源序列的排序不需要查询其余部分的PLinq。
注意:PLINQ 暂留查询其余部分的顺序强制施加运算符生成的序列顺序。 也就是说,OrderBy 和 ThenBy 等运算符被视为后跟 AsOrdered 调用。
使用ForAll
遍历查询结果
var res1 = from int x in arr1.AsParallel() where x % 2 == 0 select x;
List<int> ls1 = new List<int>();
res1.ForAll((x) => ls1.Add(x));
类似于:
List<int> ls2 = new List<int>();
foreach(int item in res1)
{
ls2.Add(item);
}
其中,
- 使用
foreach
遍历有一个合并使用Linq时满足条件的select
值输出的过程,ForAll
则没有。 ForAll
为并行遍历。foreach
为顺序遍历。
以下是foreach
和ForAll
在查询执行方面的区别:
在 PLINQ 中,在必须保留查询结果的最终排序,以及以按串行方式处理结果时,例如当为每个元素调用 Console.WriteLine 时,则可以使用 foreach。 为了在无需顺序暂留以及可自行并行处理结果时更快地执行查询,请使用 ForAll 方法执行 PLINQ 查询。 ForAll 不执行最终的这一合并步骤。
可取消PLinq查询
PLINQ 在 .NET Framework 4 中与取消类型集成在一起。因此,与顺序 LINQ to Objects 查询不同,可以取消 PLINQ 查询。 若要创建可取消 PLINQ 查询,请在查询中使用 WithCancellation 运算符,并提供 CancellationToken 实例作为参数。 如果令牌上的 IsCancellationRequested 属性设置为 true,PLINQ 就会注意到它,停止处理所有线程并抛出 OperationCanceledException。
指路:
- 如何取消PLINQ查询:https://docs.microsoft.com/zh-cn/dotnet/standard/parallel-programming/how-to-cancel-a-plinq-query
- 托管线程中的取消:https://docs.microsoft.com/zh-cn/dotnet/standard/threading/cancellation-in-managed-threads
自定义分区程序
指路官方文档(提前挖坑)
性能
在很多情况下,可以并行化查询,但是设置并行查询的开销可能会超出获得的性能收益。 如果查询不执行大量的计算,或者如果数据源较小,则 PLINQ 查询的速度可能比顺序 LINQ to Objects 查询的速度慢。
参考资料
- MSDN:并行Linq