PLINQ 中的合并选项

当查询作为并行方式执行时,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 查询运算符可能会忽略用户提供的合并选项。 某些查询运算符,例如 ReverseOrderBy,必须在所有元素都已生成并重新排序后才能生成任何元素。 因此,当ParallelMergeOptions在一个包含运算符(例如Reverse)的查询中使用时,只有在该运算符产生其结果之后,合并行为才会在查询中应用。

某些运算符处理合并选项的能力取决于源序列的类型,以及查询前面是否 AsOrdered 使用了运算符。 ForAll 始终是 NotBuffered; 它立即产出其元素。 OrderBy 始终为 FullyBuffered;它必须先对整个列表进行排序,再生成元素。

另请参阅