【c#基础】并行Linq

并行:指在同一时刻,有多条指令在多个处理器上同时执行

并发:指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在宏观上具有多个进程同时执行的效果,但在微观上并不是同时执行的,只是把时间分成若干段,使多个进程快速交替的执行

并发是逻辑上的同时发生(simultaneous),而并行是物理上的同时发生。 

串行:A和B两个任务运行在一个CPU线程上,在A任务执行完之前不可以执行B。即,在整个程序的运行过程中,仅存在一个运行上下文,即一个调用栈一个堆。程序会按顺序执行每个指令。

1:并行Linq

System.Linq名称空间包含的类ParallelEnumerable可以分解查询的工作,使其分布在多个线程上。

尽管Enumerable类给IEnumerable接口定义了扩展方法。但ParallelEnumerable类的大多数扩展方法是ParallelQuery类的扩展。AsParallel()方法,它扩展了IEnumerable接口,返回ParallelQuery类,所以正常的集合类可以以并行方式查询。

2:并行查询

 对于可以放在CPU缓存中的小集合,并行Linq看不出效果。

AsParallel()方法用ParallelEnumerable()类定义,以扩展IEnumerable接口,所以可以对简单的数组调用它,AsParallel()方法返回ParallelQuery。因为返回的类型,所以编译器选择的Where()方法是ParallelEnumerable.Where()。与Enumerable类的实现代码相反,对于ParallelEnumerable类,查询时分区的,以便多个线程可以同时处理该查询。

集合可以分为多个部分,其中每个部分由不同的线程处理,以赛选其选项。,完成分区工作后,需要合并,获得所有部分的总和

1  static IEnumerable<int> SampleData()
2         {
3             const int arraySize = 50000000;
4             var r = new Random();
5             return Enumerable.Range(0, arraySize).Select(x => r.Next(140)).ToList();
6         }
 static void Main(string[] args)
        {
            var data = SampleData();
            var res = data.AsParallel().Where(x => Math.Log(x) < 4).Select(x => x).Average();
            Console.WriteLine(res);
           
        }

data.AsParallel()这行代码从任务管理器,中可以看出系统的所有CPU利用率上升,使用多个cpu在运行,如果系统上没有多个cpu,就不会看到并行版本带来的改进。

下图为使用AsParaller()后的cpu使用率和所有处理器的百分比 12行代码使用率为13.04%左右

【c#基础】并行Linq_第1张图片

 

 未使用AsParallel()

【c#基础】并行Linq_第2张图片

 

 从上面两张截图可以看出 使用了AsParallel()的CPU的百分比是比未使用AsParallel()的百分比高的,但是cpu的使用率则是相反的。使用了AsParallel()的使用率会比没使用的会更低。

电脑配置说明:运行该例子的是双核cpu。

3:分区器

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

Partitioner类用System.Collection.Concurrent名称空间定义,并且有不同的变体。

Create()方法接受实现了IList类的数组或对象。根据这一点,以及Boolean类型的参数loadBalance和该方法的一些重载版本,会返回一个不同的Partitioner类型。对于数组,使用派生基类OrderablePartitioner的DynamicPartitionerForArray类和StaticPartitionerForArray类。

 

1  var result =
2                 (from x in Partitioner.Create(data.ToList(), true).AsParallel() where Math.Log(x) < 4 select x).Average();
3             Console.WriteLine(result);

也可以调用WithExecutionMode()和WithDegreeOfParallelism()方法,来影响并行机制。

对于WithExecutionMode()方法可以传递ParallelExecutionMode的一个Default值或者ForceParallelism值。 默认情况下,并行Linq避免使用系统开销很高的并行机制。

对于WithDegreeOfParallelism()方法可以传递一个整数值,以指定应并行运行的最大任务数。

查询不应使用全部CPU,这个方法会很有用

4:取消

.Net提供乐乐一种标准方式,来取消长时间运行的任务,这也适用于并行Linq。

要取消长时间运行的查询,可以给查询添加WithCancellation()方法,并传递一个CancellationToken令牌作为参数。

CancellationToken令牌从CancellationTokenSource类中创建。该查询在单独的线程中运行。

在该线程中,捕获一个OperationCanceledException类型异常。如果取消了查询。就触发这个异常,在主线程中,调用CancellationTokenSource类的Cancel()方案可以取消任务。

 1    var cts = new CancellationTokenSource();
 2             Task.Run(() => {
 3                 try
 4                 {
 5                     var res = (from x in data.AsParallel().WithCancellation(cts.Token) where Math.Log(x) < 4 select x)
 6                         .Average();
 7                     Console.WriteLine($"query finished,sum:{res}");
 8                 }
 9                 catch (Exception e)
10                 {
11                     Console.WriteLine(e.Message);
12                 }}, cts.Token);
13             Console.WriteLine("query start");
14             Console.Write("Cancel?");
15             string input = Console.ReadLine();
16             if (input != null && input.ToLower().Equals("y"))
17             {
18                 cts.Cancel();
19             }

 

你可能感兴趣的:(【c#基础】并行Linq)