如果不保证顺序,我将获得一个随机的订单(Orders)数据集,它们可能是(也可能不是)应该先发货的五个订单,为了确保得到前五个订单,我需要在查询中增加一个Order By子句,按照日期对查询结果进行排序,当然这样就会丢掉PLINQ的一些好处。
因为结果来自多个线程,难免不会出现异常,PLINQ不能明白“上一条”和“下一条”的概念,如果在你的循环中刚好要用到下一条项目的值时,完全有可能会遭遇错误的处理,为了让订单中的项目按照原始数据源中的顺序处理,你需要在查询中增加AsOrdered扩展。
例如,如果我想将低于某一运费的所有订单打包到一起处理,我可能会写下面这样一个循环:
totFreight += ord.Freight
If totFreight > FreightChargeLimit Then
Exit For
End If
shipOrders.Add(ord)
Next
由于并行处理返回的项目顺序不可预知,因此进入批处理的订单可能是随机的,为了保证按照原始数据源中的顺序处理返回的结果,我必须给数据源加上AsOrdered扩展。
Where o.RequiredDate > Now
Select o
TPL(任务并行库)介绍
如果你的处理不是由LINQ查询驱动的,你可以使用借鉴了PLINQ的TPL技术,从根本上看,TPL让你创建可并行执行的循环,如果你的计算机是四核的,一个循环可能用1/3的时间就完成了。
如果不使用TPL,你可能会像下面这样处理Orders集合中的所有元素:
o.RequiredDate.Value.AddDays(2)
Next
如果使用TPL,你调用Parallel类的ForEach方法,通过Lambda表达式来处理集合中的项目:
le.Orders, Sub(o)
o.RequiredDate.Value.AddDays(2)
End Sub)
通过使用Parallel ForEach,每个方法的实例可以在独立的处理器上同时处理,如果每个操作需要1毫秒,并且有足够的处理器存在,所有的订单就可以在1毫秒内处理,而不是1毫秒乘以订单数量的时间。
任何复杂的处理放在Lambda表达式中都会变得很难阅读,因此你要经常想到在你的Lambda表达式中调用下面这样一些方法:
le.Orders, Sub(o)
ExtendOrders(o)
End Sub)
...
Sub ExtendOrders(ByVal o As Order)
o.RequiredDate.Value.AddDays(2)
End Sub
从本质上讲,TPL将集合中的成员分配给独立的任务,这些任务又被分配到所有处理核心上执行,每个任务完成时释放掉代码,TPL调度器从执行队列中取出另一个任务开始执行,你也可以根据索引值使用For方法创建一个循环。
当你创建自定义任务时你才会感觉到TPL的强大之处,任务创建好后使用它的Start方法启动,但它更容易使用Task类的静态工厂对象(Factory),它的StartNew方法可以创建并启动任务(Task),你只需要通过一个Lambda表达式就可以使用StartNew方法,如果你的函数要返回一个值,你可以使用Task对象的Generic版本指定返回的类型。