LINQ最基本的功能就是对集合进行遍历查询,并在此基础上对元素进行操作。仔细推敲会发现,并行编程简直就是专门为这一类应用准备的。因此,微软 专门为LINQ拓展了一个类ParallelEnumerable(该类型也在命名空间System.Linq中),它所提供的扩展方法会让LINQ支持 并行计算,这就是所谓的PLINQ。
传统的LINQ计算是单线程的,PLINQ则是并发的、多线程的,我们通过下面这个示例就可以看出这个区别:
- static void Main(string[] args)
- {
- List<int> intList = new List<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
- var query = from p in intList select p;
- Console.WriteLine("以下是LINQ顺序输出:");
- foreach (int item in query)
- {
- Console.WriteLine(item.ToString());
- }
- Console.WriteLine("以下是PLINQ并行输出:");
- var queryParallel = from p in intList.AsParallel() select p;
- foreach (int item in queryParallel)
- {
- Console.WriteLine(item.ToString());
- }
- }
LINQ的输出会按照intList中的索引顺序打印出来。而PLINQ的输出是杂乱无章的。
并行输出还有另外一种方式可以处理,那就是对queryParallel求ForAll:
- queryParallel.ForAll((item) =>
- {
- Console.WriteLine(item.ToString());
- });
但是这种方法会带来一个问题,如果要将并行输出后的结果进行排序,ForAll会忽略掉查询的AsOrdered请求。如下所示:
- var queryParallel = from p in intList.AsParallel().AsOrdered() select p;
- queryParallel.ForAll((item) =>
- {
- Console.WriteLine(item.ToString());
- });
AsOrdered方法可以对并行计算后的队列进行重新组合,以便保持顺序。可是在ForAll方法中,它所完成的输出仍是无序的。如果要保持AsOrdered方法的需求,我们应当始终使用第一种并行方式,即:
- var queryParallel = from p in intList.AsParallel().AsOrdered() select p;
- foreach (int item in queryParallel)
- {
- Console.WriteLine(item.ToString());
- }
在并行查询后再进行排序,会牺牲掉一定的性能。一些扩展方法默认会对元素进行排序,这些方法包括:OrderBy、 OrderByDescending、ThenBy和ThenByDescending。在实际的使用中,一定要注意到各种方式之间的差别,以便程序按照 我们的设想运行。
还有一些其他的查询方法,比如Take。如果我们这样编码:
- foreach (int item in queryParallel.Take(5))
- {
- Console.WriteLine(item.ToString());
- }
在顺序查询中,会返回前5个元素。但是在PLINQ中,会选出5个无序的元素。
建议在对集合中的元素项进行操作的时候使用PLINQ代替LINQ。但是要记住,不是所有并行查询的速度都会比顺序查询快,在对集合执行某些方法 时,顺序查询的速度会更快一点,如方法ElementAt等。在开发中,我们应该仔细辨别这方面的需求,以便找到最佳的解决方案。