首页 | 新闻 | 交流 | 问吧 | 文档 | 手册 | 下载 | 博客

C#4.0入门 第三章 Task类与PLINQ—第三页 PLINQ的使用(转)

作者:  时间: 2011-06-11

  
PLINQ为查询的并行处理提供了可能性。要并行处理查询操作只要插入AsParallel方法就可以了。


但是问题就出现在并行本身。也就是说,如果没有很好的关于同步的设计的话,出来的数据的顺序可能是乱的,没有经过排序的。因为并行执行的时候,可以同时处理很多数据,但并不确保哪些数据是首先处理结束的。所以,象下面的例子中,如果在查询表达式中插入AsParallel方法,查询出来的结果有可能会发生改变。


不经过并行处理的查询操



 
using System;
using System.Linq;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
int[] ar = { 1, 2, 3 };
var q1 = from n in ar select n;
int sum = 1;
foreach (var n in q1)
{
Console.WriteLine("sum={0}×{1}+{1}", sum, n);
sum = sum * n + n;
}
Console.WriteLine("result {0}", sum);
}
}


运行结果




 sum=1×1+1
sum=2×2+2
sum=6×3+3
result 21


如果象下例那样在这段代码的查询表达式中,加入AsParallel方法,会怎么样呢?


在查询表达式中加入AsParallel方法



 
var q1 = from n in ar.AsParallel() select n;


运行结果(结果因操作系统等而不确定,此处是结果的一个举例。)




 sum=1×1+1
sum=2×3+3
sum=9×2+2
result 20


这种情况下,虽然计算方法并没有发生错误,但参与计算的数字的顺序发生了变化,所以计算结果也就不一样了。为了保证计算内容与计算结果完全一致,需要象下例那样加上锁专用对象及锁语句。


加上锁专用对象及锁语句



 
object o = new object();
foreach (var n in q1)
{
lock (o)
{
Console.WriteLine("sum={0}×{1}+{1}", sum, n);
sum = sum * n + n;
}
}


排序方法为,象下例那样在方法链中加上AsOrdered方法,从而保证顺序不变。


排序方法



 
var q1 = from n in ar.AsParallel().AsOrdered() select n;


但是,因为要保持顺序不变,肯定就意味着会对处理的速度有所影响。如果要使性能发挥到最大,还是要执行跟排序无关的查询。


另外,“object o = new
object();”这句代码看起来可能有点奇怪。因为要将object类型进行实例化,一般终归希望实例化后的对象是带有某种功能的。虽然lock语句中如果使用某个具体类型的对象,也可以实现排他性操作,但是如果为了解决同步问题,使用object类型的对象是最安全的。


综上所述,如果涉及到同步与顺序问题,滥用PLINQ会容易产生问题。但是如果不涉及到这两方面,是可以享受到在多核时代因为使用PLINQ而提升性能的好处的。

本章总结
1.任务(Task类)与线程(Thread类)是两个似是而非的概念。使用Task类是很简单容易的。

2.可以预先安排任务结束时所要执行的功能。

3.可以简单地等待多个任务的结束。

4.只要添加AsParallel方法就可以对查询操作进行并行处理了。

5.PLINQ在同步问题上存在问题,可能不能保持顺序不变。

6.为了保持顺序不变,需要添加AsOrdered方法,但是对性能产生影响