并行控制
本文认为并行LINQ(PLINQ)总是好的,例如,首先选择是否要并行运行,然后决定如何将多个子查询分配给多个线程,你可以使用With*扩展控制PLINQ的行为。
在使用调试工具的时候,你会发现PLINQ不是并行执行查询的,你可以传递ParallelExecutionMode .ForceParallelism值给WithExecutionMode方法让其强制并行执行查询。
WithExecutionMode(ParallelExecutionMode.ForceParallelism)
如果你想指定线程的数量(例如,你想让一或多个处理核心闲置),你可以使用WithDegreeOfParallelism方法,下面的代码示例将线程数限制为3。
WithDegreeOfParallelism(3)
你也可以使用cancellation结束处理过程,首先创建一个CancellationTokenSource对象,然后将其传递给WithCancellation扩展。
ords = From o In le.Orders.AsParallel.
WithCancellation(ctx.Token)
Where o.RequiredDate > Now
Select o
For Each ord As Order In ords
totFreight += ord.Freight
If totFreight > FreightChargeLimit Then
ctx.Cancel()
End If
Next
如果你正在处理For…Each循环中的PLINQ查询结果,调用cancellation会自动退出循环。
如果在一个订单(Order)上的处理过程不和另一个订单上的处理过程共享状态,可以使用ForAll循环进一步提高响应,ForAll可以用于支持Lambda表达式的PLINQ查询结果集,它和For…Each循环不一样,For…Each只在程序的主线程中执行的,而传递给ForAll方法的操作是在PLINQ查询产生的独立查询线程上执行的。
ord.RequiredDate.Value.AddDays(2)
End Sub)
此外,For…Each循环是在它自己的线程中串行执行的,而ForAll中的代码是在检索订单的线程上并行执行的。
管理顺序
虽然和SQL类似,但PLINQ不保证顺序,PLINQ子查询返回结果的顺序依赖于各个线程不可预知的响应时间,例如下面这个查询是为了获得将要先发货的五个订单。
Where o.RequiredDate > Now
Select o
Take (5)

图 1 PLINQ给TPL中的功能添加查询分析和标准查询操作,TPL提供管理操作系统底层线程需要的基本的结构和调度