PLINQ 是语言集成查询(Language Integrate Query , LINQ)的并行实现(P 表示并行)。本章将介绍其编程的各个方面以及与之相关的一些优缺点。
PLINQ 介绍 | Microsoft Learn了解如何使用 .NET 中的 PLINQ 并行执行查询。 PLINQ 代表并行语言集成查询 (LINQ)。https://learn.microsoft.com/zh-cn/dotnet/standard/parallel-programming/introduction-to-plinq 本教程对应学习工程:魔术师Dix / HandsOnParallelProgramming · GitCode
1、PLINQ 查询
在 System.Linq 命名空间和 System.Core 程序集中可以使用 ParallelEnumerable 类。除了支持 LINQ 定义的大多数标准查询运算符(Query Operator)之外,ParallelEnumerable 类还包含许多支持并行执行的方法:
-
AsParallel:这是并行所需的种子方法。
-
AsSequential:通过更改并行行为来启动并行查询的顺序执行。
-
AsOrdered:默认情况下,PLINQ 不保留执行任务和返回结果的顺序。可以通过调用 AsOrdered 方法来保留此顺序。
-
AsUnordered:这是 ParallelQuery 的默认行为,可以被 AsOrdered 方法覆盖。通过调用此方法,可以将行为从有序更改为无序。
-
ForAll:使查询执行可以并行执行。
-
Aggregate:此方法可用于聚合并行查询中各种线程局部分区的结果。
-
WithDegreeOfParallelism:使用此方法时,可以指定用于并行查询执行的处理器的最大数量。
-
WithExecutionMode:使用此方法可以强制并行执行查询,或者让 PLINQ 决定是否需要按顺序或并行执行查询。
扩展阅读:
详解c# 并行计算_C#教程_脚本之家本文主要介绍了并行计算的简单使用,并行循环的中断和跳出、并行循环中为数组/集合添加项、返回集合运算结果/含有局部变量的并行循环、、PLinq(Linq的并行计算)等相关内容。https://www.jb51.net/article/202397.htmLINQPad - The .NET Programmer's Playgroundhttps://www.linqpad.net/
2、PLINQ 查询简单示例
2.1、并行查询
这里主要是为了演示并行查询,因此逻辑很简单,就是给定一个数,从列表中找是否有这个数:
private void SearchNumberParallel()
{
//首先获取一个 0~10000的顺序列表:
var L = Utils.GetOrderList(10000);
//获取输入查询的目标值
int targetNumber = commonPanel.GetInt32Parameter();
//之后开始查询
int i = 0;
L.AsParallel().ForAll(x =>
{
if (targetNumber == x)
{
Debug.Log($"已经查到:{x},在第{i} 次查询中找到!");
}
i++;
});
Debug.Log("查询完成");
}
如果我们是顺序遍历,那么显然我们输入什么参数,就应该是第几次查到:输入0就是第0次查到,输入999就是在第999次查到。我们运行上述代码,输入1024:
显然这个顺序就变了,并不是 1024 次。
2.2、顺序查询
那如果我们要保持查询顺序怎么办呢?只需要稍作修改API即可:
L.AsParallel().AsSequential().All(x =>
{
if (targetNumber == x)
{
Debug.Log($"已经查到:{x},在第{i} 次查询中找到!");
return false;//跳出循环
}
i++;
return true;//继续循环
});
这样查询结果就如下所示了:
这里只是 PLINQ 查询语句的简单示例。
3、在并行执行时保持顺序
PLINQ 将按并行方式执行工作项,在默认情况下,它并不关心保留项目顺序以提高并行查询性能。但是,有时源集合项目中执行顺序很重要,此时需要保持顺序。
在并行执行项目时保留顺序对性能有直接影响。因为我们需要在分区中保留原始顺序,并确保合并项目时顺序保持一致。
3.1、使用 AsOrdered 方法
按照书上写的示例,代码如下:
private void SearchNumberAsOrdered()
{
//首先获取一个 0~10000的顺序列表:
var L = Utils.GetOrderList(10);
//之后开始查询
var orderd = L.AsParallel().AsOrdered().Select(x =>
{
Debug.LogWarning($"Select : {x}");
return x;
}).ToList();
string foreachStr = "ForEachOrder : ";
orderd.ForEach(x =>
{
foreachStr = $"{foreachStr} - {x}";
});
Debug.Log(foreachStr);
}
这个代码当然是顺序的…… 因为 orderd 的类型就是 List<T>。打印结果如下:
如果我们把 AsOrdered 去掉呢?实际上结果也是一样的:
说实话我不知道他这个 AsOrderd 生效在什么地方……
如何:在 PLINQ 查询中控制排序 | Microsoft Learn详细了解:如何:在 PLINQ 查询中控制排序https://learn.microsoft.com/zh-cn/dotnet/standard/parallel-programming/how-to-control-ordering-in-a-plinq-query 后来我发现问题所在了,那就是取值的列表,我是自己生成的一个 List<int> ,而示例(无论是书上的还是微软官方网站)一直用的是 Enumerable.Range 生成的迭代器,我换成 Enumerable.Range(0,10) 一下子就和书上对上了。等于说是 List<T>本身对多线程有处理,就是顺序、线程安全的?
List表示可通过索引访问的对象的强类型列表。 提供用于对列表进行搜索、排序和操作的方法。 https://learn.microsoft.com/zh-cn/dotnet/api/system.collections.generic.list-1?view=netstandard-2.1#thread-safety 翻了下官方文档,好像确实是……
3.2、使用 AsUnOrdered 方法
一旦在 PLINQ 上使用了 AsOrderd,查询就会按顺序执行。但是一直顺序执行会损失性能,因此我们希望在顺序工作完成之后,改为无序查询以提高性能。
示例代码如下:
//首先获取一个 0~10的顺序列表:
var L = Enumerable.Range(0, 10);
orderd= L.AsParallel().AsOrdered().Take(5).AsUnordered().Select(x => x * x).ToList();
这个操作就是取前5项元素,这一步是顺序的,然后再无序返回运算结果。执行结果就是 0~4 各自的平方无序返回为一个 List 。
(未完待续)
本文只是简单介绍了一下 PLINQ 语句的一些基本使用。
本教程对应学习工程:魔术师Dix / HandsOnParallelProgramming · GitCode