如何使用TPL数据流库指定无序执行块?

时间:2014-04-06 12:16:59

标签: c# task-parallel-library tpl-dataflow

我想设置TransformBlock并行处理其项目。因此,我将ExecutionDataflowBlockOptions.MaxDegreeOfParallelism设置为> 1.我不关心消息的顺序,但documentation说:

  

如果指定的最大并行度大于1,则会同时处理多个消息,因此,可能无法按接收顺序处理消息。但是,从块输出消息的顺序将正确排序。

“正确排序”是否表示如果队列中有一条消息需要较长的处理时间,则在处理完这一条消息之前不会输出更多消息?

如果是这样,我如何指定不关心排序的执行块(例如TransformBlock)?或者我必须在消费端指定我不关心订购?

2 个答案:

答案 0 :(得分:5)

库中没有这样的块,但您可以通过组合ActionBlockBufferBlock轻松创建一个。类似的东西:

public static IPropagatorBlock<TInput, TOutput>
    CreateUnorderedTransformBlock<TInput, TOutput>(
    Func<TInput, TOutput> func, ExecutionDataflowBlockOptions options)
{
    var buffer = new BufferBlock<TOutput>(options);
    var action = new ActionBlock<TInput>(
        async input =>
        {
            var output = func(input);
            await buffer.SendAsync(output);
        }, options);

    action.Completion.ContinueWith(
        t =>
        {
            IDataflowBlock castedBuffer = buffer;

            if (t.IsFaulted)
            {
                castedBuffer.Fault(t.Exception);
            }
            else if (t.IsCanceled)
            {
                // do nothing: both blocks share options,
                // which means they also share CancellationToken
            }
            else
            {
                castedBuffer.Complete();
            }
        });

    return DataflowBlock.Encapsulate(action, buffer);
}

这样,一旦ActionBlock处理了某个项目,它就会立即转移到BufferBlock,这意味着不会维护订单。

这段代码的一个问题是它没有很好地观察集合BoundedCapacity:实际上,这个块的容量是选项中设置容量的两倍(因为这两个块中的每一个都有一个分开的能力)。

答案 1 :(得分:0)

(将commentNPNelson升级为答案)

DataflowBlockOptions类包含一个可配置属性EnsureOrdered(于2016年引入),该属性确定是否将接收到的消息的顺序保留在块的输出中。默认情况下,此属性为true。将此属性设置为false可使该块在消息被处理后立即传播,由于传播速度更快且开销减少,因此增加了管道的吞吐量。