TPL DataFlow TransformBlock输入顺序和输出顺序项不匹配

时间:2012-10-10 17:55:36

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

编辑:根据svick的建议,我用一个简单的TransformBlock替换了自定义IPropagatorBlock,但是,我仍然看到输入项的顺序和输出项的顺序不匹配。在我传入的TransformBlock实例化和Func下面:

quoteBuffer = new TransformBlock<Tuple<Symbol, int>, List<Quote>>(syncExecution, new ExecutionDataflowBlockOptions { SingleProducerConstrained = true,  MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded });

//Function that performs Sync Processing
Func<Tuple<Symbol, int>, List<Quote>> syncExecution = new Func<Tuple<Symbol, int>, List<Quote>>(partitionTuple =>
{
    Symbol symbol = partitionTuple.Item1;
    int partitionIndex = partitionTuple.Item2;

    //Read Binary Data
    byte[] byteArray = binaryDataReaders[symbol].ReadBytes(partitionIndex);

    //Deserialize and return quote list
    List<Quote> quoteList = dataInterfaces[symbol].Deserialize(symbol, byteArray);

    return quoteList;
});

这就是我发布到变换块的方式:

quoteBuffer.SendAsync(new Tuple<Symbol, int>(symbol, counter));

原始问题:

有人用以下自定义变换块帮助了我。想法是发布/发送同步TInput并以异步方式对TInput进行操作,而自定义转换块在返回转换后的项目时保留已发布项目的顺序。

例如,如果在相应的顺序中发布1,2,3并且变换函数对每个输入进行平方并返回项目,则正确的输出值和顺序应为1,4,9,而不管3变换中的哪一个操作完成时。

但是,我怀疑代码有错误,因为输出顺序不正确。更糟糕的是,混乱的订单位置是随机的,这使得调试变得更加困难,但却反映了这样一个事实:显然,将输入元素转换为输出元素的任务完全不同。

有人可以看看,并可能提供一些我在这里缺少的提示吗?感谢

public static IPropagatorBlock<TInput, TOutput> CreateConcurrentOrderedTransformBlock<TInput, TOutput>(Func<TInput, TOutput> transform)
    {
        var queue = new TransformBlock<Task<TOutput>, TOutput>(t => t);

        var processor = new ActionBlock<Tuple<TInput, Action<TOutput>>>(
            tuple => tuple.Item2(transform(tuple.Item1)),
            new ExecutionDataflowBlockOptions
            {
                MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded
            });

        var enqueuer = new ActionBlock<TInput>(
            async item =>
            {
                var tcs = new TaskCompletionSource<TOutput>();
                await processor.SendAsync(
                    new Tuple<TInput, Action<TOutput>>(item, tcs.SetResult));
                await queue.SendAsync(tcs.Task);
            });

        enqueuer.Completion.ContinueWith(
            _ =>
            {
                queue.Complete();
                processor.Complete();
            });

        return DataflowBlock.Encapsulate(enqueuer, queue);
    }

1 个答案:

答案 0 :(得分:0)

我回答我自己的问题,因为我发现了导致所有这些麻烦的错误。从我的lambda表达式中可以看出,我读取了数据块中的字节数组,这意味着只要并行度设置为> 1字节,就会同时从物理磁盘中读取同一文件中的数组。这显然非常混淆了读取字节的位置。我用br.basestream.seek(...)设置读操作的起点,并通过br.readbytes(numberBytes)读取字节。由于几个操作同时影响文件中的位置,二进制读取器很可能以无序方式读取字节,这导致混乱。

我通过将二进制读取器拉出lambda表达式来解决问题,而是将读取字节数组传递到表达式中,并仅使用并发进行反序列化和合并/排序,从而解决了问题。是的,变换块保留了顺序。感谢svick在tpl数据流方面分享您的丰富专业知识。