当查询作为并行方式执行时,PLINQ 会对源序列进行分区,以便多个线程可以同时处理不同的部分,通常是在单独的线程上。 如果要在一个线程中使用结果,例如在 foreach
(Visual Basic 中为 For Each
)循环中,那么必须将每个线程的结果合并回一个序列中。 PLINQ 执行的合并类型取决于查询中存在的运算符。 例如,对结果施加新顺序的运算符必须缓冲所有线程中的所有元素。 从使用线程(以及应用用户)的角度来看,完全缓冲查询可能会运行很长时间,才能生成第一个结果。 默认情况下,其他运算符会部分缓冲;它们以批处理方式生成结果。 默认不缓冲的一个运算符是 ForAll。 它立即从所有线程生成所有元素。
通过使用此方法 WithMergeOptions ,如以下示例所示,可以向 PLINQ 提供一个提示,指示要执行的合并类型。
var scanLines = from n in nums.AsParallel()
.WithMergeOptions(ParallelMergeOptions.NotBuffered)
where n % 2 == 0
select ExpensiveFunc(n);
Dim scanlines = From n In nums.AsParallel().WithMergeOptions(ParallelMergeOptions.NotBuffered)
Where n Mod 2 = 0
Select ExpensiveFunc(n)
有关完整示例,请参阅 “如何:在 PLINQ 中指定合并选项”。
如果特定查询不支持请求的选项,则只会忽略该选项。 在大多数情况下,无需为 PLINQ 查询指定合并选项。 但是,在某些情况下,可以通过测试和度量来发现查询在非默认模式下执行得最好。 此选项的常见用途是强制区块合并运算符流式传输其结果,以便提供响应更快速的用户界面。
ParallelMergeOptions
ParallelMergeOptions 枚举包括以下选项,用于针对受支持的查询形状,指定在一个线程上使用结果时如何生成查询的最终输出:
Not Buffered
该 NotBuffered 选项会导致每个处理元素在生成后立即从每个线程返回。 这种行为类同于“流式传输”输出。 AsOrdered如果查询中存在运算符,
NotBuffered
则保留源元素的顺序。 尽管NotBuffered
在结果可用后立即开始生成结果,但生成所有结果的总时间可能仍比使用其他合并选项之一更长。Auto Buffered
此选项 AutoBuffered 会导致查询将元素收集到缓冲区中,然后定期将缓冲区内容一次性生成到使用线程。 这类似于将源数据分成“区块”而不是采用
NotBuffered
的“流式”处理行为。AutoBuffered
可能需要花费比NotBuffered
更长的时间,才能在使用线程中生成第一个元素。 缓冲区的大小和确切的生成行为不可配置,可能会有所不同,具体取决于与查询相关的各种因素。FullyBuffered
该 FullyBuffered 选项会导致在生成任何元素之前缓冲整个查询的输出。 使用此选项时,第一个元素在消耗线程上可用之前可能需要更长的时间,但完成结果可能仍比使用其他选项更快地生成。
支持合并选项的查询运算符
下表列出了支持所有合并选项模式的运算符,但受指定限制的约束。
操作员 | 限制 |
---|---|
AsEnumerable | 没有 |
Cast | 没有 |
Concat | 只包含 Array 或 List 源的无序查询。 |
DefaultIfEmpty | 没有 |
OfType | 没有 |
Reverse | 只包含 Array 或 List 源的无序查询。 |
Select | 没有 |
SelectMany | 没有 |
Skip | 没有 |
Take | 没有 |
Where | 没有 |
所有其他 PLINQ 查询运算符可能会忽略用户提供的合并选项。 某些查询运算符,例如 Reverse 和 OrderBy,必须在所有元素都已生成并重新排序后才能生成任何元素。 因此,当ParallelMergeOptions在一个包含运算符(例如Reverse)的查询中使用时,只有在该运算符产生其结果之后,合并行为才会在查询中应用。
某些运算符处理合并选项的能力取决于源序列的类型,以及查询前面是否 AsOrdered 使用了运算符。 ForAll 始终是 NotBuffered; 它立即产出其元素。 OrderBy 始终为 FullyBuffered;它必须先对整个列表进行排序,再生成元素。