简体   繁体   English

TPL数据流从所有传入节点(多个生产者,1个消费者)创建聚合结果数组

[英]TPL Dataflow create aggregated result array from all incoming nodes (multiple producer, 1 consumer)

Please note following code sample. 请注意以下代码示例。 I need an aggregator node, that can be linked to any number of sources, waites for all sources to send one message and then combines those in a result[]. 我需要一个聚合器节点,该节点可以链接到任意数量的源,等待所有源发送一条消息,然后将它们组合成一个result []。

This should be obvious and straigt forward, but somehow I do not find a solution. 这应该是显而易见的,并且是向前发展的,但是以某种方式我找不到解决方案。 I checked JoinBlock and TransformaterBlock, but both seem unfitting. 我检查了JoinBlock和TransformaterBlock,但两者似乎都不适合。

using System;
using System.Threading.Tasks.Dataflow;

namespace ConsoleApp2
{
    internal class Program
    {
        private static readonly uint _produceCount = 0;
        private static void Main(string[] args)
        {

            BufferBlock<string> p1 = new BufferBlock<string>();
            BufferBlock<string> p2 = new BufferBlock<string>();

            // a block is required that accepts n sources as input, waits for all inputs to arrive, and then creates a result array from all inputs

            ActionBlock<string[]> c1 = new ActionBlock<string[]>((inputs) =>
            {
                Console.WriteLine(String.Join(',', inputs));
            });

            p1.Post("Produce 1.1");
            p2.Post("Produce 2.1");

            // desired output:
            // "Produce 1.1, Produce 2.1"
            // actually the order is of no importance at this time

        }


    }
}

[Edit] Further clarification: I would like to have a block that: - dynamically await all source-notes (at the point in time the first message arrives) to complete and aggregate the result to pass to follower nodes [编辑]进一步说明:我想提供一个块:-动态等待所有源注释(在第一条消息到达的时间点)以完成并汇总结果以传递到关注者节点

If you know your sources beforehand, I'd use a JoinBlock together with a TransformBlock . 如果您事先知道您的来源,我将使用JoinBlockTransformBlock You would have to create a BufferBlock for each source. 您将必须为每个源创建一个BufferBlock

First, the JoinBlock waits for one message from each source and packs them in one tuple. 首先, JoinBlock等待来自每个源的一条消息并将它们打包在一个元组中。 Then the TransformBlock creates a result array from the intermediate tuple. 然后, TransformBlock从中间元组创建一个结果数组。

If you do not know your sources beforehand, you need to explain how you expect your new block to know when to produce a result. 如果您事先不知道来源,则需要说明如何期望新块知道何时产生结果。 That logic should then be put into a custom block, probably in the form of a TransformManyBlock<string,string[]> . 然后应该将该逻辑放入自定义块中,可能采用TransformManyBlock<string,string[]>

If you want to join a dynamic number of sources, you can create an unlimited join block like this: 如果要加入动态数量的源,可以创建一个无限的加入块,如下所示:

private static void Main()
{
    var source1 = new BufferBlock<string>();
    var source2 = new BufferBlock<string>();
    var source3 = new BufferBlock<string>();
    var aggregator = CreateAggregatorBlock( 3 );
    var result = new ActionBlock<string[]>( x => Console.WriteLine( string.Join( ", ", x ) ) );
    source1.LinkTo( aggregator );
    source2.LinkTo( aggregator );
    source3.LinkTo( aggregator );
    aggregator.LinkTo( result );

    source1.Post( "message 1" );
    source2.Post( "message 2" );
    source3.Post( "message 3" );

    Console.ReadLine();
}

private static TransformManyBlock<string, string[]> CreateAggregatorBlock( int sources )
{
    var buffer = new List<string>();
    return new TransformManyBlock<string, string[]>( message => {
        buffer.Add( message );
        if( buffer.Count == sources )
        {
            var result = buffer.ToArray();
            buffer.Clear();
            return new[] {result};
        }
        return Enumerable.Empty<string[]>();
    } );
}

This assumes your sources produce messages at the same rate. 假设您的消息源以相同的速率产生消息。 If that's not the case, you need to but the identity of the source next to the message and have a buffer for each source. 如果不是这种情况,则您需要在消息旁边加上源的标识,并为每个源都有一个缓冲区。

You can use a non-greedy BatchBlock for this. 您可以为此使用非贪婪的BatchBlock By being non-greedy each source will contribute one item to the batch. 由于不贪心,每个来源将为该批次贡献一个物品。 This was originally suggested here . 最初是在这里建议的 And here's a tested example: Note as proof, source1 is sent multiple items that don't show up in the batch: 这是一个经过测试的示例:请注意,作为证明, source1发送了多个未显示在批处理中的项目:

public class DataAggregator
{
    private BatchBlock<string> batchBlock = new BatchBlock<string>(5, new GroupingDataflowBlockOptions() { Greedy = false });
    private ActionBlock<string[]> writer = new ActionBlock<string[]>(strings => strings.ToList().ForEach(str => Console.WriteLine(str)));
    private BufferBlock<string> source1 = new BufferBlock<string>();
    private BufferBlock<string> source2 = new BufferBlock<string>();
    private BufferBlock<string> source3 = new BufferBlock<string>();
    private BufferBlock<string> source4 = new BufferBlock<string>();
    private BufferBlock<string> source5 = new BufferBlock<string>();

    public DataAggregator()
    {
        source1.LinkTo(batchBlock, new DataflowLinkOptions() { PropagateCompletion = true });
        source2.LinkTo(batchBlock, new DataflowLinkOptions() { PropagateCompletion = true });
        source3.LinkTo(batchBlock, new DataflowLinkOptions() { PropagateCompletion = true });
        source4.LinkTo(batchBlock, new DataflowLinkOptions() { PropagateCompletion = true });
        source5.LinkTo(batchBlock, new DataflowLinkOptions() { PropagateCompletion = true });
        batchBlock.LinkTo(writer, new DataflowLinkOptions() { PropagateCompletion = true });
    }

    [Test]
    public async Task TestPipeline()
    {
        source1.Post("string1-1");
        source1.Post("string1-2");
        source1.Post("string1-3");
        source2.Post("string2-1");
        source3.Post("string3-1");
        source4.Post("string4-1");
        source5.Post("string5-1");
        //Should print string1-1 string2-1 string3-1 string4-1 string5-1
        source1.Complete();
        source2.Complete();
        source3.Complete();
        source4.Complete();
        source5.Complete();
        await writer.Completion;
    }
}

Output: 输出:

string1-1
string2-1
string3-1
string4-1
string5-1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM