知识点 - .Net - LINQ 并行查询

 

并行查询

首先填充一个大的数组

const int arraySize = 100000000;
var data = new int[arraySize];
var r = new Random ( );
for ( int i = 0; i < arraySize; i++ ) {
    data[i] = r.Next ( 40 );
}

使用筛选

var sum = ( from x in data.AsParallel ( )
                  where x < 20
                  select x ).Sum ( );

与平时LINQ查询一样,编译器会修改语法,以调用 AsParallel()、Where()、Select()和Sum()方法。AsParallel()方法用 ParallelEnumerable 类定义,以扩展 IEnumerable<T> 接口,所以可以对简单的数组调用它。AsParallel()方法返回 ParallelQuery<TSource>。因为返回的类型,所以编译器选择的Where()方法是 ParallelEnumerable.Where(),而不是 Enumerable.Where() 。在下面的代码中, Select()和 Sum()方法也来自 ParallelEnumerable 类。与Enumerable类的实现代码相反,对于ParallelEnumerable类,查询是分区的,以便 多个线程可以同时处理该查询。数组可以分为多个部分,其中每个部分由不同的线程处理,以筛选其余项。完成分区的工作后,就需要合并,获得所有部分的总和。

 var sum = data.AsParallel ( ).Where ( x => x < 20 ).Select ( x => x ).Sum ( );

运行这行代码会启动任务管理器,这样就可以看出系统的所有CPU都在忙碌。如果删除AsParallel()方法,就不可能使用多个CPU。当然,如果系统上没有多个CPU,就不会看到并行版本带来的改进。

 

 

分区器

  AsParallel()方法不仅扩展了 IEnumerable<T>接口,还扩展了Partitioner类。通过它,可以影响要创建的分区。

  Partitioner类用System.Collection.Concurrent名称空间定义,并且有不同的变体。Create()方法接受实现了 IList<T>类的数组或对象。根据这一点,以及类型的参数 loadBalance 和该方法的一些重载版本,会返回一个不同的Partitioner类型。对于数组, .NET 4 包含派生自抽象基类OrderablePartitioner<TSource>的DynamicPartitionerForArray<TSource>类和StaticPartitionerForArray<TSource>类和StaticParitionerForArray<TSource>类。

  修改上面一小节的代码,手工创建一个分区器,而不是使用默认的分区器:

var sum = ( from x in Partitioner.Create ( data, true ).AsParallel ( )
            where x < 20
            select x ).Sum ( );

  也可以调用WithExecutionMode()和WithDegreeOfParallelism()方法,来影响并行机制。对于WithExecutionMode()方法可以传递ParallelExecutionMode()的一个Default值或者ForceParallelism值。默认情况下,并行LINQ避免使用系统开销很高的并行机制。对于WithDegreeOfParallelism()方法,可以传递一个整数值,以指定应并行运行的最大任务数。

 

测试

 

 

 

并行化的效率比原有的方式快出了3倍左右

 

 

总结:

并行查询是利用了CPU的并行化来做的查询, 所以建议是:

数据量小时不建议用并行查询, 

CPU只有单核时不建议使用并行查询,  

否则查询效率可能还不如原来的查询

posted @ 2018-05-21 23:04  `Laimic  阅读(205)  评论(0)    收藏  举报